1. extern在函数声明中是什么意思?
它可以用作一种格式上的提示表明函数的定义可能在另一个源文件中,但在

extern int f(); 和 int f(); 之间并没有实质的区别。

补充:extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。此外extern也可用来进行链接指定。也就是说extern有两个作用:

第 一,当它与"C"一起连用时,如: extern "C" void fun(int a, int b);则告诉编译器在编译fun这个函数名时按着C的规则去翻译相应的函数名而不是C++的,C++的规则在翻译这个函数名时会把fun这个名字变得面目 全非,可能是fun@aBc_int_int#%$也可能是别的,这要看编译器的"脾气"了(不同的编译器采用的方法不一样),为什么这么做呢,因为 C++支持函数的重载;
第二,当extern不与"C"在一起修饰变量 或函数时,如在头文件中: extern int g_Int; 它的作用就是声明函数或全局变量的作用范围的关键字,其声明的函数和变量可以在本模块活其他模块中使用,记住它是一个声明不是定义!也就是 说B模块(编译单元)要是引用模块(编译单元)A中定义的全局变量或函数时,它只要包含A模块的头文件即可,在编译阶段,模块B虽然找不到该函数或变量, 但它不会报错,它会在连接时从模块A生成的目标代码中找到此函数。

2. 怎样建立和理解非常复杂的声明?例如定义一个包含N个指向返回指向字符的指针的函数的指针的数组?
这个问题至少有以下3种答案:

(1)char *(*(*a[N])())();

(2)用typedef逐步完成声明:

typedef char *pc;        /* 字符指针 */
typedef pc fpc();        /* 返回字符指针的函数 */
typedef fpc *pfpc;       /* 上面函数的指针 */
typedef pfpc fpfpc();    /* 返回函数指针的函数 */
typedef fpfpc *pfpfpc;   /* 上面函数的指针 */
pfpfpc a[N];             /* 上面指针的数组 */
(3)使用cdecl程序,它可以把英文翻译成C或者把C翻译成英文:

通过类型转换,cdecl也可以用于解释复杂的声明,指出参数应该进入哪一对括号(如同在上述的复杂函数定义中)。

补充:cdecl程序非常有用,它可以在C语言的声明和英语之间进行转换。它可以解释一个现存的C语言声明,cdecl 程序可以帮助你分析复杂的声明。

一个在线的cdecl.

3. 为什么struct x { ... }; x thestruct; 不对?
C不是C++。结构标签不能自动生成类型。

补充:也就是在C++中这样是对的。在C中不想用struct只能借助于typedef。

4. NULL可以确保是0,但空指针(null)却不一定。

5. 数组和指针的区别是什么?
数组自动分配空间,但是不能重分配或改变大小。指针必须明确赋值以指向分配的空间(可能使用malloc),但是可以随意重新赋值(即指向不同的对象),同时除了表示一个内存块的基址之外,还有许多其它的用途。
由于数组和指针所谓的等价性,数组和指针经常看起来可以互换,而事实上指向malloc分配的内存块的指针通常被看作一个真正的数组(也可以用[]引用)。但是,要小心sizeof。

6. 当我向一个接受指针的指针的函数传入二维数组的时候,编译器报错了。
数组蜕化为指针的规则不能递归应用。数组的数组(即二维数组)蜕化为数组的指针,而不是指针的指针。数组指针常常令人困惑,需要小心对待:

如果你向函数传递二维数组:
int array[NROWS][NCOLUMNS];
f(array);
那么函数的声明必须匹配:
void f(int a[][NCOLUMNS]){ ... }
或者
void f(int (*ap)[NCOLUMNS]){ ... }  /* ap是个数组指针 */

在第一个声明中,编译器进行了通常的从“数组的数组”到“数组的指针”的隐式转换;第二种形式中的指针定义显而易见。
因为被调函数并不为数组分配地址,所以它并不需要知道总的大小,所以行数NROWS可以省略。但数组的宽度依然重要,所以列维度NCOLUMNS(对于三维或多维数组,相关的维度)必须保留。

如果一个函数已经定义为接受指针的指针,那么几乎可以肯定直接向它传入二维数组毫无意义。

7. 为什么这段代码不行?
char *str;
gets(str);
printf("%s\n", str);

str没有指向任何合法的位置,换言之,我们不知道指针str指向何处。因为局部变量没有初始化,通常包含垃圾信息,所以甚至都不能保证str是一个合法的指针。
改正方法:用局部变量或用malloc()分配str缓冲区。

8. 我刚才试了这样的代码char *p; strcpy(p, "abc"); 而它运行正常?怎么回事?为什么它没有崩溃?
未初始化的指针p所指向的随机地址恰好对你来说是可写的,而且显然也没有用于什么关键的数据。
char *p; 编译器只分配了足够容纳指针本身的内存; 也就是说,这种情况下,你分配了sizeo(char*)个字节的内存。但你还没有分配任何让指针指向的内存,因此此时p所指向的内存可能是垃圾信息也可能是可写的区域,没有崩溃说明是后者。

9. 下面关于itoa的实现为什么是错误的?如何改正?
char *itoa(int n)
{
     char retbuf[20];   // error
     sprintf(retbuf, "%d", n);
     return retbuf;     // error
}

这样在编译的时候会出现这样的警告信息“warning C4172: returning address of local variable or temporary”,说明retbuf的地址是临时变量,是暂时的,函数返回时就没有了也就是不能直接返回。

一种解决方案是把返回缓冲区声明为静态变量:
static char retbuf[20];

总结:若要返回字符串或其它集合,则返回指针必须是静态分配的缓冲区,或者调用者传入的缓冲区,或者用malloc()获得的内存,但不能是局部(自动)数组。

10. 为什么在调用free()之后指针没有变空?使用(赋值,比较)释放之后的指针有多么不安全?
当你调用free()的时候,传入指针指向的内存被释放,但调用函数的指针值可能保持不变,因为C的按值传参语义意味着被调函数永远不会改变参数的值。严格的说,被释放的指针值是无效的,对它的任何使用,即使没有解参照,也可能带来问题,尽管作为一种实现质量的表现,多数实现都不会对无伤大雅的无效指针使用产生例外。

补充:free()过后的指针也叫做“野指针”,建议free()过后立即将指针置为NULL,详细原因请看“为什么free(re)过后re不为NULL呢?”。

11. calloc()和malloc()有什么区别?利用calloc的零填充功能安全吗?free()可以释放calloc()分配的内存吗,还是需要一个cfree()?
calloc(m, n)本质上等价于
p = malloc(m * n);
memset(p, 0, m * n);
填充的全是零,因此不能确保生成有用的空指针值或浮点零值,free()可以安全的用来释放calloc()分配的内存。

12. 在C中,sizeof('a')=4而不是1,因为C语言中的字符常数是int型,因此sizeof('a')是sizeof(int),这是与C++不同的地方。

13. C语言中没有提高标准的布尔类型,需要用#define或枚举常数定义true/false。

14. 我试图用ANSI“字符串化”预处理操作符#向信息中插入符号常量的值,但它字符串化的总是宏的名字而不是它的值。
你可以用下面这样的两步方法迫使宏既字符串化又扩展:
#define Str(x) #x
#define Xstr(x) Str(x)
#define OP plus
char *opname = Xstr(OP);
这段代码把opname置为“plus”而不是“OP”。

总结:在使用符号粘接操作符##连接两个宏的值(而不是名字)时也要采用同样的“迂回战术”。

15. memcpy()和memmove()有什么区别?
如果源和目的参数有重叠,memmove()提供有保证的行为。
而memcpy()则不能提供这样的保证,因此可以实现的更加有效率。
如果有疑问,最好使用memmvoe()。

16. 我如何在printf的格式串中输出一个'%'?我试过"\%",但是不行。
只需要重复百分号:%%。\%不行,因为\是编译器的转义字符,而这里我们的问题最终是printf的转义字符。

17. 为什么大家都说不要使用gets()?
跟fgets()不同,gets()不能被告知输入缓冲区的大小,因此不能避免缓冲区的溢出。标准库的fgets()函数对gets()作了很大的改进,尽管它仍不完善。如果真的可能输入很长的行,还是需要仔细思考,正确处理。

18. 一个粗陋但通常有效的测试NaN的方法:
#define isnan(x)    ((x) != (x))
C99提高isnan(), fpclassify()及其它一些类别的函数。

标准C函数库的源代码:
GNU工程有一个完全实现的C函数库(http://www.gnu.org/software/libc/)

19. 怎样判断机器的字节顺序是高字节在前还是低字节在前?
有个使用指针的方法:
int x = 1;
if(*(char *)&x == 1)
    printf("little-endian\n");
else
    printf("big-endian\n");
另外一个可能是用联合。

20. 由一个日期,怎样知道是星期几?
用mktime()或localtime()(注:如果tm_hour的值为0,要注意DST(夏时制)的调整);或者Zeller的congruence;或者这个由Tomohiko Sakamoto提供的优雅的代码:
int dayofweek(int y, int m, int d)  /* 0 = Sunday */
{
    static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
    y -= m < 3;
    return (y + y/4 - y/100 + y/400 + t[m - 1] + d) % 7;
}

————————————————
版权声明:本文为CSDN博主「小敏纸」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lanxuezaipiao/article/details/18953917

《你必须知道的495个C语言问题》知识笔记及补充相关推荐

  1. c语言中变量可以用x1表示没,你必须知道的495个C语言问题 读书笔记

    1.数值类型的选择:溢出特征重要而负值不重要,操作二进制位时避免符号扩展的问题,应该使用unsigned(无符号值) char 8位 -127~127 最大值255 short int 和 int 均 ...

  2. 你必须知道的495个C语言问题电子书pdf下载

    你必须知道的495个C语言问题下载链接: https://pan.baidu.com/s/1UlMaEhv2cqzlWgd_8XQ3rg 提取码获取方式:关注下面微信公众号,回复关键字: 1131

  3. 《你必须知道的261个java语言问题》读书报告——第二弹

    1.java中的线程调度 1.1 分时调度与时间调度 计算机通常只有一个CPU,在任意时刻只能一条机器指令,每个线程只有获得CPU的使用权才能执行命令.所谓的多线程的并发运行,其实是指从宏观上看.各个 ...

  4. 《你必须知道的.NET》第1章学习笔记

    面向对象中几个最基本的概念:类,对象,继承,封装和多态. 对象的出生,只是完成了对必要字段的初始化,其他数据要通过后面的操作来完成,如:属性的赋值,通过方法获取必要信息等. 对向可以分为值类型和引用类 ...

  5. 《你必须知道的.NET》第五章读书笔记

    了解.NET的类型很重要 .NET的主要类型分为值类型和引用类型 区分这2种类型 1.内存方面 值类型: 包括枚举enum 和结构 struct 内存分配: 实例通常分配在线程的堆栈上,并且不包括任何 ...

  6. python语言必背代码-Python新手必须知道的25条知识点

    原标题:Python新手必须知道的25条知识点 1.到底什么是Python?你可以在回答中与其他技术进行对比 下面是一些关键点: Python是一种解释型语言.这就是说,与C语言和C的衍生语言不同,P ...

  7. c 多文件全局变量_必须知道的C语言知识细节:C程序编译后内存到底是如何布局...

    「今天是学习C语言第 116 天」 当你选择了一种语言,意味着你还选择了一组技术.一个社区.--Joshua Bloch C语言程序编译以后,代码和数据都必须存放在内存中由CPU执行.理解C程序内存是 ...

  8. [你必须知道的.NET]第二十二回:字符串驻留(上)---带着问题思考

    发布日期:2008.8.27 作者:Anytao  © 2008 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. 说在,开篇之前 走钢丝的人,在刺激中体验快感.带着问题思考,在问 ...

  9. 作为软件工程师,你必须知道的20个常识

    1.针对面向对象的设计与分析:为了让软件有更好的可维护性,重用性以及快速开发, 简短的 OOAD 与它的 SOLID 原则对于每一个软件工程师来说都是该牢记的. 2.软件品质因素:软件工程的好坏与软件 ...

最新文章

  1. 为啥led灯用一年后暗了很多_想把卤素灯换掉,选LED灯为什么比选氙气灯
  2. Remoting系列专题---构建Remoting“防火墙”
  3. bigquery 教程_bigquery挑战实验室教程从数据中获取见解
  4. 国内外公共CDN静态资源网站大全
  5. 等式与不等式约束的序列二次规划(SQP)
  6. Spark核心RDD详述
  7. linux系统编程之信号(五)
  8. CentOS 本地ISO 挂载并配置本地软件源
  9. python多进程编程实例_Python多进程并发(multiprocessing)用法实例详解
  10. 【Django 2021年最新版教程17】数据库操作 models 存在更新 不存在新建update_or_create
  11. 自定义TextView显示指定行数
  12. 九万字的JavaWeb学习记录,从入门到入坟,更近一步
  13. android11系统原生铃声,原生系统的凤毛麟角 索尼Xperia 1 II推送Android 11体验
  14. cs 5 下载地址 、视频教程、安装教程
  15. 机器学习面试笔试求职必背!八股文(1/5)
  16. 解决eWebEditor上传图片提示:请选择一个有效的文件,支持的格式有(GIF|JPG|JPEG|BMP|PNG)!
  17. 转:从一个故事看经验、科学与伪科学
  18. docker容器创建
  19. webrtc jitterbuffer 模块分析
  20. 笔记本玩cf如何调全屏 笔记本玩cf屏幕两边有黑边怎么办

热门文章

  1. 关于python2.7的md5加密遇到的问题(TypeError: Unicode-objects must be encoded before hashing)...
  2. 可视化swing界面编辑--转载
  3. Java SE 6 新特性: 编译器 API
  4. Redis configuration
  5. 【Python】Pyecharts数据可视化模块练习
  6. Facebook首席工程负责人胡鲁辉:数字化2.0的理念、架构与业务模型丨鲸犀峰会
  7. 谷歌 Daydream 实验室:VR中学习新技能是一种怎样的体验?
  8. jvm性能调优实战 -50修复Metaspace区域内存溢出问题
  9. 深入理解分布式技术 - 消息幂等性如何保障不重复消费
  10. MyBatis源码-深入理解MyBatis Executor的设计思想