本书以问答的形式组织内容,讨论了学习或使用C语言的过程中经常遇到的一些问题。书中列出了C用户经常问的400多个经典问题,涵盖了初始化、数组、指针、字符串、内存分配、库函数、C预处理器等各个方面的主题,并分别给出了解答,而且结合代码示例阐明要点。 ### 重要知识点总结 #### 一、声明和初始化 **1.1 如何决定使用哪种整数类型?** - 在C语言中,整数类型的大小不是固定的,这取决于具体的实现。例如,`int`通常为32位系统上4字节(32位),但在某些嵌入式系统上可能是2字节(16位)。 - 选择整数类型时,应考虑程序的目标平台。对于需要确保一致性的应用,可以使用标准库`<stdint.h>`中的固定宽度整数类型,如`int32_t`或`uint16_t`。 **1.2 64位机上的64位类型是什么样的?** - 在64位平台上,整数类型的大小可能会有所不同。例如,在这种平台上,`long`类型可能为8字节(64位),而`long long`也同样是8字节。 - 使用`<stdint.h>`中的类型如`int64_t`可以确保跨平台的兼容性。 **1.3 怎样定义和声明全局变量和函数最好?** - 全局变量和函数应该尽可能地限制其作用域,以减少命名冲突的风险。可以通过将它们声明为`static`来限制其作用域到当前源文件。 - 对于全局变量,尽量避免使用,因为它们可能导致程序状态难以追踪和维护。 - 函数声明应该清晰明了,包括参数类型和返回类型。 **1.4 `extern`在函数声明中是什么意思?** - `extern`关键字用于声明一个在其他地方定义的变量或函数。在函数声明中使用`extern`并不常见,因为它不是必需的。 - 通常,`extern`用于在多个源文件间共享变量时,以确保这些变量被正确链接。 **1.5 关键字`auto`到底有什么用途?** - 在C语言中,`auto`是默认的存储类别,用于局部变量。这意味着变量在其定义的作用域内有效,并在离开该作用域后释放。 - 从C99标准开始,`auto`可以省略,因为它是默认的存储类别。 **1.6 如何定义包含指向自己的指针的结构?** - 结构体可以包含指向自身的指针成员,但必须正确声明。例如,定义结构体时应该先声明指针类型再定义结构体。 - 正确的定义方式如下: ```c struct Node { char *item; struct Node *next; }; ``` **1.7 如何建立和理解非常复杂的声明?** - 复杂的声明可以通过逐步分解来理解。例如,声明一个包含N个指向返回指向字符的指针的函数的指针的数组可以被分解为: - 指向函数的指针,函数返回`char *`。 - 包含上述类型的指针的数组。 - 示例: ```c (int (*array_of_pointers_to_functions)[N])(); ``` **1.8 函数只定义了一次,调用了一次,但编译器提示非法重定义了。** - 这种情况通常发生在多个源文件中定义相同的函数。 - 解决方法是确保每个函数只在一个源文件中定义,并在其他文件中仅声明该函数。 **1.9 `main()`的正确定义是什么?`void main()`正确吗?** - `main()`函数是程序的入口点,它的正确原型应该是`int main(void)`或`int main(int argc, char *argv[])`。 - `void main()`不是标准C的一部分,虽然有些编译器允许这样做,但不建议使用。 **1.10 对于没有初始化的变量的初始值可以作怎样的假设?** - 未初始化的变量的值是不确定的,即垃圾值。不应依赖这些值进行计算。 - 全局变量如果未初始化,默认会被初始化为零值,但这不适用于局部变量。 **1.11 为什么代码`int f(){char a[]="Hello, world!";}`不能编译?** - 字符串字面量不能直接初始化数组,因为它们具有`const`属性。正确的做法是使用`char *a = "Hello, world!";`。 **1.12 这样的初始化有什么问题?`char *p = malloc(10);`** - `malloc()`返回的是`void *`,需要显式转换为正确的类型,例如`char *p = (char *)malloc(10);`。 - 分配的空间需要初始化,例如使用`memset()`。 **1.13 以下的初始化有什么区别?** - `char a[] = "string literal";`创建了一个字符数组,并用字符串字面量初始化。 - `char *p = "string literal";`定义了一个指向字符串字面量的指针。修改这个指针指向的内容会导致未定义行为。 **1.14 如何初始化函数指针?** - 函数指针可以通过将函数名直接赋值给指针变量来初始化。 - 示例: ```c int add(int x, int y) { return x + y; } int (*func_ptr)(int, int) = add; ``` #### 二、结构、联合和枚举 **2.1 声明`struct x1 {};`和`typedef struct {} x2;`有什么不同?** - `struct x1 {};`声明了一个名为`x1`的空结构体。 - `typedef struct {} x2;`定义了一个匿名结构体,并通过`typedef`为其创建了一个别名`x2`。 **2.2 为什么`struct x {}; x the_struct;`不对?** - 这样的声明会引发编译错误,因为在声明结构体的同时尝试创建一个结构体变量。 - 正确的做法是单独声明结构体类型,然后再定义变量。 **2.3 一个结构可以包含指向自己的指针吗?** - 是的,结构体可以包含指向自身的指针成员,但必须正确声明。参考1.6中的示例。 **2.4 在C语言中实现抽象数据类型什么方法最好?** - 可以使用结构体和函数封装数据和操作。 - 通过隐藏结构体的实现细节并提供公共接口,可以实现类似于类的功能。 **2.5 在C中是否有模拟继承等面向对象程序设计特性的好方法?** - C语言本身不支持面向对象编程特性,但可以通过结构体和函数组合来模拟继承等概念。 - 例如,通过让一个结构体包含另一个结构体的实例,可以实现类似继承的效果。 **2.6 内存分配技巧使`namestr`数组看起来有多个元素,这样合法且可移植吗?** - 这种技巧通常涉及到使用`union`或动态内存分配来扩展结构体。 - 虽然可以工作,但可能不完全可移植,具体取决于编译器和平台。 **2.7 是否有自动比较结构的方法?** - 没有内置的自动比较结构的方法,但可以通过遍历结构体的成员并逐一比较来实现。 **2.8 如何向接受结构参数的函数传入常数值?** - 可以通过创建一个包含常数值的结构体实例并传递给函数来实现。 **2.9 怎样从/向数据文件读/写结构?** - 可以使用`fwrite()`和`fread()`函数来读写结构体。 - 为了保持可移植性,最好使用文本格式而不是二进制格式。 **2.10 我的编译器在结构中留下了空洞,这导致空间浪费,能否关掉填充?** - 编译器通常会对结构体成员进行对齐以优化性能。 - 可以使用`#pragma pack`指令来控制结构体成员的对齐方式,但需要注意兼容性和性能影响。 **2.11 为什么`sizeof`返回的值大于结构的期望值?** - 这是因为编译器为了性能优化而进行了成员对齐,导致结构体中出现空洞。 **2.12 如何确定域在结构中的字节偏移?** - 可以使用`offsetof`宏来确定结构体成员的偏移量。 **2.13 怎样在运行时用名字访问结构中的域?** - 在C语言中,直接通过名字访问结构体成员需要在编译时已知成员名称。 - 如果需要运行时访问,则可以使用反射机制,但这通常需要额外的库支持。 **2.14 程序运行正确,但退出时却“core dump”了,怎么回事?** - “core dump”通常表示程序存在内存访问错误。 - 应检查是否越界访问了数组或使用了未初始化的指针。 **2.15 可以初始化一个联合吗?** - 联合只能初始化其中的一个成员。 - 初始化联合时,需要指定要初始化的成员。 **2.16 枚举和一组预处理的`#define`有什么不同?** - 枚举提供了一种更安全、类型安全的方式来表示一组相关的整数常量。 - `#define`定义的常量没有类型,容易与其他整数混淆。 **2.17 有什么容易的显示枚举值符号的方法?** - 枚举值的符号可以通过宏或其他方法映射到字符串。 - 可以使用`switch`语句来实现这一功能。 #### 三、表达式 **3.1 为什么这样的代码`a[i] = i++;`不能工作?** - 这种表达式可能会导致未定义行为,因为`i`的值可能在使用之前就已经改变。 - 为了避免这种情况,可以将表达式拆分为两步。 **3.2 使用某些编译器,下面的代码`int i = 7; printf("%d\n", i++ * i++);`返回49?** - 表达式的顺序依赖于编译器的实现。 - `i`的自增发生在表达式求值的不同阶段,这可能导致不同的结果。 **3.3 对于代码`int i = 3; i = i++;`不同编译器给出不同的结果,哪个是正确的?** - 这种表达式也是未定义行为的例子。 - 结果取决于编译器的具体实现。 **3.4 这是个巧妙的表达式`a ^= b ^= a ^= b`;它不需要临时变量就可以交换a和b的值。** - 这个表达式确实可以用来交换两个变量的值。 - 但它可能存在溢出风险,特别是当变量接近其最大值时。 **3.5 我可否用括号来强制执行我所需要的计算顺序?** - 是的,使用括号可以明确指定运算的优先级。 - 括号可以帮助避免由于操作符优先级引起的错误。 **3.6 可是`&&`和`||`运算符呢?** - `&&`和`||`运算符是短路运算符,意味着如果第一个操作数就能确定结果,第二个操作数将不会被评估。 - 这些运算符主要用于逻辑判断,可以用来控制表达式的求值顺序。 **3.7 我怎样才能理解复杂表达式?“序列点”是什么?** - 复杂表达式可以通过逐部分分析来理解。 - “序列点”是在表达式中定义的特定点,确保所有之前的操作都已完成。 **3.8 对于`a[i] = i++;`我们不知道a[]的哪一个分量会被改写,但i的确会增加1,对吗?** - 是的,`i`肯定会增加1,但`a[i]`的行为是未定义的。 **3.9 `++i`和`i++`有什么区别?** - `++i`是前缀递增,立即增加`i`的值。 - `i++`是后缀递增,返回`i`的当前值,然后增加`i`。 **3.10 如果我不使用表达式的值,我应该用`++i`或`i++`来自增一个变量吗?** - 如果只是单纯递增,使用`++i`通常更高效,因为它不需要保存变量的原始值。 **3.11 为什么如下的代码`int a = 100, b = 100; long int c = a * b;`不能工作?** - 这段代码实际上可以正常工作,但需要注意整数乘法可能会溢出。 - 为了避免溢出,可以在计算之前将变量转换为更大的类型。 **3.12 我需要根据条件把一个复杂的表达式赋值给两个变量中的一个。可以用下边这样的代码吗?`((condition) ? a : b) = complicated_expression;`** - 这种写法是无效的,因为条件运算符的结果不能被赋值。 - 应该先计算表达式,再根据条件赋值给相应的变量。 以上是《你必须知道的495个C语言问题》一书提到的部分重要知识点的详细解释。这些知识点覆盖了C语言的基本概念以及在实际编程中常见的问题,对于理解和掌握C语言具有重要的指导意义。













剩余152页未读,继续阅读

- #完美解决问题
- #运行顺畅
- #内容详尽
- #全网独家
- #注释完整

- 粉丝: 0
- 资源: 1
我的内容管理 展开
我的资源 快来上传第一个资源
我的收益
登录查看自己的收益我的积分 登录查看自己的积分
我的C币 登录后查看C币余额
我的收藏
我的下载
下载帮助


最新资源
- 西门子PLC与发那科机器人协作的净水器碳芯检测系统解析
- (源码)基于Bootstrap框架的微金所管理系统.zip
- 物联网项目中高效稳定的Socket TCP服务器端通信框架及其应用
- (源码)基于Python框架的微信智能聊天机器人.zip
- FX25030071谷雨线下通用纸袋(大白360275150mm)2404文案箱唛.xlsx
- 基于ABAQUS的隧道开挖模型构建:阶梯开挖、锚杆、钢架支撑与衬砌的应用与优化
- (源码)基于C++的USB设备通信与控制库.zip
- LabVIEW在汽车齿轮齿条转向器疲劳试验台中的运动控制与数据采集分析
- (源码)基于EventOS框架的嵌入式系统测试平台.zip
- (源码)基于LPC824Lite开发板的GPIO LED切换项目.zip
- 直流无刷电机驱动器:双模式支持与开发板应用的技术详解
- (源码)基于Arduino框架的乘车安全嵌入式系统.zip
- 电机控制领域SVPWM过调制算法的MATLAB与Python仿真及其实现细节
- (源码)基于物联网的3D球体LED广告牌显示系统(POVGLOBE).zip
- 恒压供水系统中西门子224XP PLC与昆仑通态触摸屏的一拖二工变频互锁控制应用
- (源码)基于STM32F10x微控制器的IAP引导加载程序.zip


