- 浏览: 89393 次
- 性别:
- 来自: 北京
最新评论
-
hpu423:
...
JAVA 字符串常量池 -
hpu423:
[c[flash=200,200][flash=200,200 ...
JAVA 字符串常量池 -
liubang201010:
Foglight 监控OC4j自2004年左右, Oracle ...
OC4J 10.0.3 安装 配置 详解
1).类型声明错误:
typedef struct { char *item; NODEPTR next; } *NODEPTR; 但是编译器报了错误信息。难道在C语言中一个结构不能包含指向自己的指针吗?
C 语言中的结构当然可以包含指向自己的指针; [K&R2, 第6.5 节] 的讨论和例子表明了这点。NODEPTR 例子的问题是在声明next 域的时候typedef 还没有定义。为了解决这个问题, 首先赋予这个结构一个标签(“struct node”)。然后,声明“next” 域为“struct node *”, 或者分开typedef 定义和结构定义, 或者两者都采纳。
以下是一个修改后的版本:
struct node {
char *item;
struct node *next;
};
typedef struct node *NODEPTR;
至少还有三种同样正确的方法解决这个问题。在用typedef 定义互相引用的两个结构时也会产生类似的问题, 可以用同样的方法解决。
2).复杂声明的分解:(优先级从最远的开始)
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).未声明直接调用函数的问题:
在范围内没有声明就调用(也可能是第一次调用在函数的定义之前) 的函数被认为返回整(int) (且没有任何参数类型信息), 如果函数在后边声明或定义成其它类型就会导致矛盾。所有函数(非整型函数一定要) 必须在调用之前声明。
4).字符串初始化数组和字符指针的问题:
以下的初始化有什么区别?char a[] = "string literal"; char *p= "string literal"; 当我向p[i] 赋值的时候, 我的程序崩溃了。字符串常量有两种稍有区别的用法。用作数组初始值(如同在char a[] 的声明中), 它指明该数组中字符的初始值。其它情况下, 它会转化为一个无名的静态字符数组, 可能会存储在只读内存中, 这就是造成它不一定能被修改。在表达式环境中, 数组通常被立即转化为一个指针(参见第6 章), 因此第二个声明把p 初始化成指向无名数组的第一个元素。
5).声明struct x1 { . . . }; 和typedef struct { . . . } x2; 有什么不同?
第一种形式声明了一个“结构标签”; 第二种声明了一个“类型定义”。主要的区别是在后文中你需要用“struct x1” 引用第一种, 而用“x2” 引用第二种。也就是说, 第二种声明更像一种抽象类新—– 用户不必知道它是一个结构, 而在声明它的实例时也不需要使用struct 关键字。
6).是否有自动比较结构的方法
没有。编译器没有简单的好办法实现结构比较(即, 支持结构的== 操作符),这也符合C 的低层特性。简单的按字节比较会由于结构中没有用到的“空洞” 中的随机数据(参见问题2.10) 而失败; 而按域比较在处理大结构时需要难以接受的大量重复代码。
如果你需要比较两个结构, 你必须自己写函数按域比较。
7).如何确定域在结构中的字节偏移?
ANSI C 在<stddef.h> 中定义了offsetof() 宏, 用offsetof(struct s, f) 可以计算出域f 在结构s 中的偏移量。如果出于某种原因, 你需要自己实现这个功能, 可以使用下边这样的代码:
#define offsetof(type, f) ((size_t) \
((char *)&((type *)0)->f - (char *)(type *)0))
这种实现不是100% 的可移植; 某些编译器可能会合法地拒绝接受。
8).这是个巧妙的表达式: a ˆ= b ˆ= a ˆ= b 它不需要临时变量就可以交换a 和b 的值。
这不具有可移植性。它试图在序列点之间两次修改变量a, 而这是无定义的。
例如,有人报告如下代码:
int a = 123, b = 7654;
a ^= b ^= a ^= b;
在SCO 优化C 编译器(icc) 下会把b 置为123, 把a 置为0。
9).我可否用括号来强制执行我所需要的计算顺序?
一般来讲, 不行。运算符优先级和括弧只能赋予表达是计算部分的顺序. 在如下的代码中
f() + g() * h()
尽管我们知道乘法运算在加法之前, 但这并不能说明这三个函数哪个会被首先调用。如果你需要确保子表达式的计算顺序, 你可能需要使用明确的临时变量和独立的语句。
10).我怎样才能理解复杂表达式?“序列点” 是什么?
序列点是一个时间点(在整个表达式全部计算完毕之后或在||、&&、? : 或逗号运算符处, 或在函数调用之前), 此刻尘埃落定, 所有的副作用都已确保结束。
ANSI/ISO C 标准这样描述:
在上一个和下一个序列点之间, 一个对象所保存的值至多只能被表达式的计算修改一次。而且前一个值只能用于决定将要保存的值。第二句话比较费解。它说在一个表达式中如果某个对象需要写入, 则在同一表达式中对该对象的访问应该只局限于直接用于计算将要写入的值。这条规则有效地限制了只有能确保在修改之前才访问变量的表达式为合法。例如i = i+1 合法, 而a[i] = i++ 则非法.那么, 对于a[i] = i++; 我们不知道a[] 的哪一个分量会被改写,但i的确会增加1, 对吗?
不一定!如果一个表达式和程序变得未定义, 则它的所有方面都会变成未定义。
11).*p++ 自增p 还是p 所指向的变量?
后缀++ 和-- 操作符本质上比前缀一目操作的优先级高, 因此*p++ 和*(p++) 等价, 它自增p 并返回p 自增之前所指向的值。要自增p 指向的值, 使用(*p)++, 如果副作用的顺序无关紧要也可以使用++*p。
12).我有一个char * 型指针正巧指向一些int 型变量, 我想跳过它们。为什么如下的代码((int *)p)++; 不行?
在C 语言中, 类型转换意味着“把这些二进制位看作另一种类型, 并作相应的对待”; 这是一个转换操作符, 根据定义它只能生成一个右值(rvalue)。而右值既不能赋值, 也不能用++ 自增。(如果编译器支持这样的扩展, 那要么是一个错误, 要么是有意作出的非标准扩展。) 要达到你的目的可以用:
p = (char *)((int *)p + 1);
或者,因为p 是char * 型, 直接用
p += sizeof(int);
但是, 在可能的情况下, 你还是应该首先选择适当的指针类型, 而不是一味地试图李代桃僵。
13).我有一个函数extern int f(int *); 它接受指向int 型的指针。我怎样用引用方式传入一个常数?下面这样的调用f(&5); 似乎不行。
在C99 中, 你可以使用“复合常量”:
f((int[]){5});
在C99 之前, 你不能直接这样做; 你必须先定义一个临时变量, 然后把它的地址传给函数:
int five = 5;
f(&five);
C 有“按引用传递” 吗?
真的没有。
严格地讲, C 总是按值传递。你可以自己模拟按引用传递, 定义接受指针的函数, 然后在调用时使用& 操作符。事实上, 当你向函数传入数组时, 编译器本质上就是在模拟按引用传递。但是C 没有任何真正等同于正式的按引用传递或C++ 的引用参数的东西。另一方面, 类似函数的
预处理宏可以提供一种“按名称传递”的形式。
14).我看到了用指针调用函数的不同语法形式。到底怎么回事?
最初, 一个函数指针必须用* 操作符(和一对额外的括弧) “转换为” 一个“真正的” 函数才能调用:
int r, func(), (*fp)() = func;
r = (*fp)();
而函数总是通过指针进行调用的, 所有“真正的” 函数名总是隐式的退化为指针(在表达式中, 正如在初始化时一样)。这个推论表明无论fp 是函数名和函数的指针
r = fp();
ANSI C 标准实际上接受后边的解释, 这意味着* 操作符不再需要, 尽管依然允许。
15).怎样在程序里获得一个空指针?
根据语言定义, 在指针上下文中的常数0 会在编译时转换为空指针。也就是说, 在初始化、赋值或比较的时候, 如果一边是指针类型的值或表达式, 编译器可以确定另一边的常数0 为空指针并生成正确的空指针值。因此下边的代码段完全
合法:
char *p = 0;
if(p != 0)
然而, 传入函数的参数不一定被当作指针环境, 因而编译器可能不能识别未加修饰的0 “表示” 指针。在函数调用的上下文中生成空指针需要明确的类型转换,强制把0 看作指针。例如, Unix 系统调用execl 接受变长的以空指针结束的字符指针参数。它应该如下正确调用:
execl("/bin/sh", "sh", "-c", "date", (char *)0);
如果省略最后一个参数的(char *) 转换, 则编译器无从知道这是一个空指针,从而当作一个0 传入。(注意很多Unix 手册在这个例子上都弄错了。)
如果范围内有函数原型, 则参数传递变为“赋值上下文”, 从而可以安全省略多数类型转换, 因为原型告知编译器需要指针, 使之把未加修饰的0 正确转换为适当的指针。函数原型不能为变长参数列表中的可变参数提供类型。在函数调用时对所有的空指针进行类型转换可能是预防可变参数和无原型函数出问题的最安全的办法。
摘要表:
可以使用未加修饰的0: 需要显示的类型转换:
初始化 函数调用, 作用域内无原型
赋值变参 函数调用中的可变参数
比较
固定参数的函数调用且在作用域
内有原型
当C 在表达式中要求布尔值时, 如果表达式等于0 则认为该值为假, 否则为真。换言之, 只要写出
if(expr)
无论“expr” 是任何表达式, 编译器本质上都会把它当
if((expr) != 0) 处理。
如果用指针p 代替“expr” 则
if(p) 等价于if(p != 0)。
而这是一个比较上下文, 因此编译器可以看出0 实际上是一个空指针常数, 并使用正确的空指针值。这里没有任何欺骗; 编译器就是这样工作的, 并为、二者生成完全一样的代码。空指针的内部表达无关紧要。
16).我在一个源文件中定义了char a[6], 在另一个中声明了extern char *a 。为什么不行?
你在一个源文件中定义了一个字符串, 而在另一个文件中定义了指向字符的指针。extern char * 的申明不能和真正的定义匹配。类型T 的指针和类型T 的数组并非同种类型。请使用extern char a[ ]。
17).为什么作为函数形参的数组和指针申明可以互换呢?
这是一种便利。由于数组会马上蜕变为指针, 数组事实上从来没有传入过函数。允许指针参
数声明为数组只不过是为让它看起来好像传入了数组, 因为该参数可能在函数内当作数组使用。特别地, 任何声明“看起来象” 数组的参数, 例如
void f(char a[])
{ ... }
在编译器里都被当作指针来处理, 因为在传入数组的时候,那正是函数接收到的.
void f(char *a)
{ ... }
这种转换仅限于函数形参的声明, 别的地方并不适用。如果这种转换令你困惑, 请避免它; 很多程序员得出结论, 让形参声明“看上去象” 调用或函数内的用法所带来的困惑远远大于它所提供的方便。
18).现实地讲, 数组和指针地区别是什么?
数组自动分配空间, 但是不能重分配或改变大小。指针必须明确赋值以指向分配的空间(可能使用malloc), 但是可以随意重新赋值(即, 指向不同的对象), 同时除了表示一个内存块的基址之外, 还有许多其它的用途。
由于数组和指针所谓的等价性, 数组和指针经常看起来可以互换, 而事实上指向malloc 分配的内存块的指针通常被看作一个真正的数组(也可以用[ ] 引用)。但是, 要小心sizeof。
19).如何声明一个数组指针?
通常, 你不需要。当人们随便提到数组指针的时候, 他们通常想的是指向它的第一个元素的指针。
考虑使用指向数组某个元素的指针, 而不是数组的指针。类型T 的数组蜕变成类型T 的指针, 这很方便; 在结果的指针上使用下标或增量就可以访问数组中单独的成员。而真正的数组指针, 在使用下标或增量时, 会跳过整个数组, 通常只在操作数组的数组时有用—— 如果还有一点用的话。
如果你真的需要声明指向整个数组的指针, 使用类似“int (*ap)[N];” 这样的声明。其中N 是数组的大小。如果数组的大小未知, 原则上可以省略N, 但是这样生成的类型, “指向大小未知的数组的指针”, 毫无用处。
20).当我向一个接受指针的指针的函数传入二维数组的时候, 编译器报错了。
数组蜕化为指针的规则(参见问题6.3) 不能递归应用。数组的数组(即C语言中的二维数组) 蜕化为数组的指针, 而不是指针的指针。数组指针常常令人困惑, 需要小心对待;
如果你向函数传递二位数组:
int array[NROWS][NCOLUMNS];
f(array);
那么函数的声明必须匹配:
void f(int a[][NCOLUMNS])
{ ... }
或者
void f(int (*ap)[NCOLUMNS]) /* ap 是个数组指针*/
{ ... }
在第一个声明中, 编译器进行了通常的从“数组的数组” 到“数组的指针” 的隐式转换; 第二种形式中的指针定义显而易见。因为被调函数并不为数组分配地址, 所以它并不需要知道总的大小, 所以行数NROWS 可以省略。但数组的宽度依然重要, 所以列维度NCOLUMNS (对于三维或多维数组, 相关的维度) 必须保留。
如果一个函数已经定义为接受指针的指针, 那么几乎可以肯定直接向它传入二维数组毫无意义。
当数组是函数的参数时, 为什么sizeof 不能正确报告数组的大小?
编译器把数组参数当作指针对待, 因而报告的时指针的大小。
21).为什么这段代码不行?char *answer; printf("Type something:\n");gets(answer); printf("You typed \"%s\"\n", answer);
指针变量answer, 传入gets(), 意在指向保存得到的应答的位置, 但却没有指向任何合法的位置。换言之, 我们不知道指针answer 指向何处。因为局部变量没有初始化, 通常包含垃圾信息, 所以甚至都不能保证answer 是一个合法的指针。
改正提问程序的最简单方案是使用局部数组, 而不是指针, 让编译器考虑分配的问题:
#include <stdio.h>
#include <string.h>
char answer[100], *p;
printf("Type something:\n");
fgets(answer, sizeof answer, stdin);
if((p = strchr(answer, ’\n’)) != NULL)
*p = ’\0’;
printf("You typed \"%s\"\n", answer);
本例中同时用fgets() 代替gets(), 以便array 的结束符不被改写。不幸的是, 本例中的fgets() 不会象gets() 那样自动地去掉结尾的nn。也可以用malloc() 分配answer 缓冲区。
22).我的strcat() 不行.
我试了char *s1 = "Hello, "; char *s2 ="world!"; char *s3 = strcat(s1, s2); 但是我得到了奇怪的结果。
这里主要的问题是没有正确地为连接结果分配空间。C 没有提供自动管理的字符串类型。C 编译器只为源码中明确提到的对象分配空间(对于字符串, 这包括字符数组和串常量)。程序员必须为字符串连接这样的运行期操作的结果分配足够的空间, 通常可以通过声明数组或调用malloc() 完成。
strcat() 不进行任何分配; 第二个串原样不动地附加在第一个之后。因此, 一种解决办法是把第一个串声明为数组:
char s1[20] = "Hello, ";
由于strcat() 返回第一个参数的值, 本例中为s1,而s3 实际上是多余的; 在strcat() 调用之后, s1 包含结果。提问中的strcat() 调用实际上有两个问题: s1 指向的字符串常数, 除了空间不足以放入连接的字符串之外, 甚至都不一定可写。
23).static的作用
1.static如果是修饰函数或全局变量,该变量会被隐藏,成为Internal linkage的变量,其他文件的代码中无法访问到。
2.static如果在函数中修饰局部变量,将会保持变量内容的持久性,在该函数外依然可以访问到变量的内容。
3.static还有一个作用是被修饰的变量会被初始化为0,和全局变量类似,不同的是函数内部被static修饰的变量也会初始化为0。
24).我的程序总是崩溃, 显然在malloc 内部的某个地方。但是我看不出哪里有问题。是malloc() 有bug 吗?
很不幸, malloc 的内部数据结构很容易被破坏, 而由此引发的问题会十分棘手。最常见的问题来源是向malloc 分配的区域写入比所分配的还多的数据; 一个常见的bug 是用malloc(strlen(s)) 而不是strlen(s) + 1。其它的问题还包括使用指向已经释放了的内存的指针, 释放未从malloc 获得的内存, 或者两次释放同一个指针, 或者试图重分配空指针.
25).为什么在调用free() 之后指针没有变空?使用(赋值, 比较) 释放之后的指针有多么不安全?
当你调用free() 的时候, 传入指针指向的内存被释放, 但调用函数的指针值可能保持不变, 因为C 的按值传参语义意味着被调函数永远不会永久改变参数的值。
严格的讲, 被释放的指针值是无效的, 对它的任何使用, 即使没有解参照, 也可能带来问题, 尽管作为一种实现质量的表现, 多数实现都不会对无伤大雅的无效指针使用产生例外。
2.当我malloc() 为一个函数的局部指针分配内存时, 我还需要用free() 明确的释放吗?
是的。记住指针和它所指向的东西是完全不同的。局部变量在函数返回时就会释放, 但是在指针变量这个问题上, 这表示指针被释放, 而不是它所指向的对象。用malloc() 分配的内存直到你明确释放它之前都会保留在那里。一般地, 对于每一个malloc() 都必须有个对应的free() 调用。
3.我在分配一些结构, 它们包含指向其它动态分配的对象的指针。我在释放结构的时候, 还需要释放每一个下级指针吗?
是的。一般地, 你必须分别向free() 传入malloc() 返回的每一个指针, 仅仅一次(如果它的确要被释放的话)。一个好的经验法则是对于程序中的每一个malloc() 调用, 你都可以找到一个对应的free() 调用以释放malloc() 分配的内存。
26).alloca() 是什么?为什么不提倡使用它?
在调用alloca() 的函数返回的时候, 它分配的内存会自动释放。也就是说, 用alloca 分配的内存在某种程度上局部于函数的“堆栈帧” 或上下文中。alloca() 不具可移植性, 而且在没有传统堆栈的机器上很难实现。当它的返回值直接传入另一个函数时会带来问题, 如fgets(alloca(100), 100, stdin)。由于这些原因, alloca() 不合标准, 不宜使用在必须广泛移植的程序中, 不管它可能多么有用。既然C99 支持变长数组(VLA), 它可以用来更好的完成alloca() 以前的任务。
2.calloc() 和malloc() 有什么区别?利用calloc 的零填充功能安全吗?free() 可以释放calloc() 分配的内存吗, 还是需要一个cfree()?
calloc(m, n) 本质上等价于p = malloc(m * n);memset(p, 0, m * n);
填充的零是全零, 因此不能确保生成有用的空指针值或浮点零值。free() 可以安全地用来释放calloc() 分配的内存。
27).为什么strcat(string, ’!’); 不行?
字符和字符串的区别显而易见, 而strcat() 用于连接字符串。
C 中的字符用它们的字符集值对应的小整数表示。字符串用字符数组表示; 通常你操作的是字符数组的第一个字符的指针。二者永远不能混用。要为一个字符串增加!, 需要使用
strcat(string, "!");
2.我在检查一个字符串是否跟某个值匹配。为什么这样不行?char *string; . . . if(string == "value") { /* string matches ”value”
*/ . . . }
C 中的字符串用字符的数组表示, 而C 语言从来不会把数组作为一个整体操作(赋值, 比较等)。上面代码段中的== 操作符比较的是两个指针—— 指针变量string 的值和字符串常数“value” 的指针值—— 看它们是否相等, 也就是说, 看它们是否指向同一个位置。它们可能并不相等, 所以比较决不会成功。
要比较两个字符串, 一般使用库函数strcmp():
if(strcmp(string, "value") == 0) {
/* string matches "value" */
3.如果我可以写char a[] = "Hello, world!"; 为什么我不能写
char a[14]; a = "Hello, world!";
字符串是数组, 而你不能直接用数组赋值。可以使用strcpy() 代替:
strcpy(a, "Hello, world!");
}
4.我认为我的编译器有问题: 我注意到sizeof(’a’) 是4 而不是1 (即,不是sizeof(char))。
可能有些令人吃惊, C语言中的字符常数是int 型, 因此sizeof(’a’) 是sizeof(int),这是另一个与C++ 不同的地方。
28).因为在C 语言中所有的非零值都被看作“真”, 是不是把TRUE 定义为1 很危险?如果某个内置的函数或关系操作符“返回” 不是1 的其它值怎么办?
C 语言中的确任何非零值都都被看作真, 但这仅限于“输入”, 也就是说, 仅限于需要布尔值的地方。内建操作符生成布尔值时, 可以保证为1 或0。因此, 这样的测试
if((a == b) == TRUE)
能如愿运行(只要TRUE 为1), 但显然这很傻。事实上, 跟TRUE 和FALSE的跟TRUE 和FALSE 的显示比较都不合适, 因为有些库函数(如isupper(),isalpha() 等) 在成功时返回非零值, 但不一定为1。(再说, 如果你认为“if((a == b)== TRUE)” 比“if(a == b)” 好, 为什么就此打住呢?为什么不使用“if(((a == b)== TRUE) == TRUE)” 呢?) 一般规则是只在向布尔变量赋值或函数参数中才使用TRUE 和FALSE (或类似的东西), 或者用于函数的返回值, 但决不用于比较。
预处理宏TRUE 和FALSE (当然还有NULL) 只是用于增加代码可读性, 而不是因为其值可能改变。
尽管使用TRUE 和FALSE 这样的宏(或者YES 和NO) 看上去更清楚, 布尔值和定义在C 语言中的复杂性让很多程序员觉得TRUE 和FALSE 宏不过更令人迷惑, 因而更喜欢使用1 和0。
if语句、while语句中的控制表达式的值也不能是结构体类型。严格来说,可以做算术运算的类型称为算术类型(Arithmetic Type),算术类型包括整型和浮点型。可以表示零和非零,可以参与逻辑与、或、非运算或者做控制表达式的类型称为标量类型(Scalar Type),标量类型包括算术类型和以后要讲的指针类型。
29).书写多语句宏的最好方法是什么?
通常的目标是书写一个象包含一个单独的函数调用语句的宏。这意味着“调用者” 需要提供最终的分号, 而宏体则不需要。因此宏体不能为简单的括弧包围的复合语句, 因为如果这样, 调用的时候就会发生语法错(明显是一个单独语句, 但却多了一个额外的分号), 就像在if/else 语句的if 分支中多了一个else 分句一样。(方便在if中调用宏函数,而不会造成语法错误)
所以, 传统的结局方案就是这样使用:
#define MACRO(arg1, arg2) do { \
/* declarations */ \
stmt1; \
stmt2; \
/* ... */ \
} while(0) /* 没有结尾的; */
当调用者加上分号后, 宏在任何情况下都会扩展为一个单独的语句。优化的编译器会去掉条件为0 的“无效” 测试或分支, 尽管lint 可能会警告。如果宏体内的语句都是简单语句, 没有声明或循环, 那么还有一种技术, 就是写一个单独的, 用一个或多个逗号操作符分隔的表式。
30).我第一次把一个程序分成多个源文件, 我不知道该把什么放到.c 文件, 把什么放到.h 文件。
作为一般规则, 你应该把这些东西放入头(.h) 文件中:
² 宏定义(预处理#defines)
² 结构、联合和枚举声明
² typedef 声明
² 外部函数声明(参见问题1.4)
² 全局变量声明
当声明或定义需要在多个文件中共享时, 尤其需要把它们放入头文件中。特别是, 永远不要把外部函数原型放到.c 文件中。
另一方面, 如果定义或声明为一个.c 文件私有(static), 则最好留在.c 文件中。
typedef struct { char *item; NODEPTR next; } *NODEPTR; 但是编译器报了错误信息。难道在C语言中一个结构不能包含指向自己的指针吗?
C 语言中的结构当然可以包含指向自己的指针; [K&R2, 第6.5 节] 的讨论和例子表明了这点。NODEPTR 例子的问题是在声明next 域的时候typedef 还没有定义。为了解决这个问题, 首先赋予这个结构一个标签(“struct node”)。然后,声明“next” 域为“struct node *”, 或者分开typedef 定义和结构定义, 或者两者都采纳。
以下是一个修改后的版本:
struct node {
char *item;
struct node *next;
};
typedef struct node *NODEPTR;
至少还有三种同样正确的方法解决这个问题。在用typedef 定义互相引用的两个结构时也会产生类似的问题, 可以用同样的方法解决。
2).复杂声明的分解:(优先级从最远的开始)
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).未声明直接调用函数的问题:
在范围内没有声明就调用(也可能是第一次调用在函数的定义之前) 的函数被认为返回整(int) (且没有任何参数类型信息), 如果函数在后边声明或定义成其它类型就会导致矛盾。所有函数(非整型函数一定要) 必须在调用之前声明。
4).字符串初始化数组和字符指针的问题:
以下的初始化有什么区别?char a[] = "string literal"; char *p= "string literal"; 当我向p[i] 赋值的时候, 我的程序崩溃了。字符串常量有两种稍有区别的用法。用作数组初始值(如同在char a[] 的声明中), 它指明该数组中字符的初始值。其它情况下, 它会转化为一个无名的静态字符数组, 可能会存储在只读内存中, 这就是造成它不一定能被修改。在表达式环境中, 数组通常被立即转化为一个指针(参见第6 章), 因此第二个声明把p 初始化成指向无名数组的第一个元素。
5).声明struct x1 { . . . }; 和typedef struct { . . . } x2; 有什么不同?
第一种形式声明了一个“结构标签”; 第二种声明了一个“类型定义”。主要的区别是在后文中你需要用“struct x1” 引用第一种, 而用“x2” 引用第二种。也就是说, 第二种声明更像一种抽象类新—– 用户不必知道它是一个结构, 而在声明它的实例时也不需要使用struct 关键字。
6).是否有自动比较结构的方法
没有。编译器没有简单的好办法实现结构比较(即, 支持结构的== 操作符),这也符合C 的低层特性。简单的按字节比较会由于结构中没有用到的“空洞” 中的随机数据(参见问题2.10) 而失败; 而按域比较在处理大结构时需要难以接受的大量重复代码。
如果你需要比较两个结构, 你必须自己写函数按域比较。
7).如何确定域在结构中的字节偏移?
ANSI C 在<stddef.h> 中定义了offsetof() 宏, 用offsetof(struct s, f) 可以计算出域f 在结构s 中的偏移量。如果出于某种原因, 你需要自己实现这个功能, 可以使用下边这样的代码:
#define offsetof(type, f) ((size_t) \
((char *)&((type *)0)->f - (char *)(type *)0))
这种实现不是100% 的可移植; 某些编译器可能会合法地拒绝接受。
8).这是个巧妙的表达式: a ˆ= b ˆ= a ˆ= b 它不需要临时变量就可以交换a 和b 的值。
这不具有可移植性。它试图在序列点之间两次修改变量a, 而这是无定义的。
例如,有人报告如下代码:
int a = 123, b = 7654;
a ^= b ^= a ^= b;
在SCO 优化C 编译器(icc) 下会把b 置为123, 把a 置为0。
9).我可否用括号来强制执行我所需要的计算顺序?
一般来讲, 不行。运算符优先级和括弧只能赋予表达是计算部分的顺序. 在如下的代码中
f() + g() * h()
尽管我们知道乘法运算在加法之前, 但这并不能说明这三个函数哪个会被首先调用。如果你需要确保子表达式的计算顺序, 你可能需要使用明确的临时变量和独立的语句。
10).我怎样才能理解复杂表达式?“序列点” 是什么?
序列点是一个时间点(在整个表达式全部计算完毕之后或在||、&&、? : 或逗号运算符处, 或在函数调用之前), 此刻尘埃落定, 所有的副作用都已确保结束。
ANSI/ISO C 标准这样描述:
在上一个和下一个序列点之间, 一个对象所保存的值至多只能被表达式的计算修改一次。而且前一个值只能用于决定将要保存的值。第二句话比较费解。它说在一个表达式中如果某个对象需要写入, 则在同一表达式中对该对象的访问应该只局限于直接用于计算将要写入的值。这条规则有效地限制了只有能确保在修改之前才访问变量的表达式为合法。例如i = i+1 合法, 而a[i] = i++ 则非法.那么, 对于a[i] = i++; 我们不知道a[] 的哪一个分量会被改写,但i的确会增加1, 对吗?
不一定!如果一个表达式和程序变得未定义, 则它的所有方面都会变成未定义。
11).*p++ 自增p 还是p 所指向的变量?
后缀++ 和-- 操作符本质上比前缀一目操作的优先级高, 因此*p++ 和*(p++) 等价, 它自增p 并返回p 自增之前所指向的值。要自增p 指向的值, 使用(*p)++, 如果副作用的顺序无关紧要也可以使用++*p。
12).我有一个char * 型指针正巧指向一些int 型变量, 我想跳过它们。为什么如下的代码((int *)p)++; 不行?
在C 语言中, 类型转换意味着“把这些二进制位看作另一种类型, 并作相应的对待”; 这是一个转换操作符, 根据定义它只能生成一个右值(rvalue)。而右值既不能赋值, 也不能用++ 自增。(如果编译器支持这样的扩展, 那要么是一个错误, 要么是有意作出的非标准扩展。) 要达到你的目的可以用:
p = (char *)((int *)p + 1);
或者,因为p 是char * 型, 直接用
p += sizeof(int);
但是, 在可能的情况下, 你还是应该首先选择适当的指针类型, 而不是一味地试图李代桃僵。
13).我有一个函数extern int f(int *); 它接受指向int 型的指针。我怎样用引用方式传入一个常数?下面这样的调用f(&5); 似乎不行。
在C99 中, 你可以使用“复合常量”:
f((int[]){5});
在C99 之前, 你不能直接这样做; 你必须先定义一个临时变量, 然后把它的地址传给函数:
int five = 5;
f(&five);
C 有“按引用传递” 吗?
真的没有。
严格地讲, C 总是按值传递。你可以自己模拟按引用传递, 定义接受指针的函数, 然后在调用时使用& 操作符。事实上, 当你向函数传入数组时, 编译器本质上就是在模拟按引用传递。但是C 没有任何真正等同于正式的按引用传递或C++ 的引用参数的东西。另一方面, 类似函数的
预处理宏可以提供一种“按名称传递”的形式。
14).我看到了用指针调用函数的不同语法形式。到底怎么回事?
最初, 一个函数指针必须用* 操作符(和一对额外的括弧) “转换为” 一个“真正的” 函数才能调用:
int r, func(), (*fp)() = func;
r = (*fp)();
而函数总是通过指针进行调用的, 所有“真正的” 函数名总是隐式的退化为指针(在表达式中, 正如在初始化时一样)。这个推论表明无论fp 是函数名和函数的指针
r = fp();
ANSI C 标准实际上接受后边的解释, 这意味着* 操作符不再需要, 尽管依然允许。
15).怎样在程序里获得一个空指针?
根据语言定义, 在指针上下文中的常数0 会在编译时转换为空指针。也就是说, 在初始化、赋值或比较的时候, 如果一边是指针类型的值或表达式, 编译器可以确定另一边的常数0 为空指针并生成正确的空指针值。因此下边的代码段完全
合法:
char *p = 0;
if(p != 0)
然而, 传入函数的参数不一定被当作指针环境, 因而编译器可能不能识别未加修饰的0 “表示” 指针。在函数调用的上下文中生成空指针需要明确的类型转换,强制把0 看作指针。例如, Unix 系统调用execl 接受变长的以空指针结束的字符指针参数。它应该如下正确调用:
execl("/bin/sh", "sh", "-c", "date", (char *)0);
如果省略最后一个参数的(char *) 转换, 则编译器无从知道这是一个空指针,从而当作一个0 传入。(注意很多Unix 手册在这个例子上都弄错了。)
如果范围内有函数原型, 则参数传递变为“赋值上下文”, 从而可以安全省略多数类型转换, 因为原型告知编译器需要指针, 使之把未加修饰的0 正确转换为适当的指针。函数原型不能为变长参数列表中的可变参数提供类型。在函数调用时对所有的空指针进行类型转换可能是预防可变参数和无原型函数出问题的最安全的办法。
摘要表:
可以使用未加修饰的0: 需要显示的类型转换:
初始化 函数调用, 作用域内无原型
赋值变参 函数调用中的可变参数
比较
固定参数的函数调用且在作用域
内有原型
当C 在表达式中要求布尔值时, 如果表达式等于0 则认为该值为假, 否则为真。换言之, 只要写出
if(expr)
无论“expr” 是任何表达式, 编译器本质上都会把它当
if((expr) != 0) 处理。
如果用指针p 代替“expr” 则
if(p) 等价于if(p != 0)。
而这是一个比较上下文, 因此编译器可以看出0 实际上是一个空指针常数, 并使用正确的空指针值。这里没有任何欺骗; 编译器就是这样工作的, 并为、二者生成完全一样的代码。空指针的内部表达无关紧要。
16).我在一个源文件中定义了char a[6], 在另一个中声明了extern char *a 。为什么不行?
你在一个源文件中定义了一个字符串, 而在另一个文件中定义了指向字符的指针。extern char * 的申明不能和真正的定义匹配。类型T 的指针和类型T 的数组并非同种类型。请使用extern char a[ ]。
17).为什么作为函数形参的数组和指针申明可以互换呢?
这是一种便利。由于数组会马上蜕变为指针, 数组事实上从来没有传入过函数。允许指针参
数声明为数组只不过是为让它看起来好像传入了数组, 因为该参数可能在函数内当作数组使用。特别地, 任何声明“看起来象” 数组的参数, 例如
void f(char a[])
{ ... }
在编译器里都被当作指针来处理, 因为在传入数组的时候,那正是函数接收到的.
void f(char *a)
{ ... }
这种转换仅限于函数形参的声明, 别的地方并不适用。如果这种转换令你困惑, 请避免它; 很多程序员得出结论, 让形参声明“看上去象” 调用或函数内的用法所带来的困惑远远大于它所提供的方便。
18).现实地讲, 数组和指针地区别是什么?
数组自动分配空间, 但是不能重分配或改变大小。指针必须明确赋值以指向分配的空间(可能使用malloc), 但是可以随意重新赋值(即, 指向不同的对象), 同时除了表示一个内存块的基址之外, 还有许多其它的用途。
由于数组和指针所谓的等价性, 数组和指针经常看起来可以互换, 而事实上指向malloc 分配的内存块的指针通常被看作一个真正的数组(也可以用[ ] 引用)。但是, 要小心sizeof。
19).如何声明一个数组指针?
通常, 你不需要。当人们随便提到数组指针的时候, 他们通常想的是指向它的第一个元素的指针。
考虑使用指向数组某个元素的指针, 而不是数组的指针。类型T 的数组蜕变成类型T 的指针, 这很方便; 在结果的指针上使用下标或增量就可以访问数组中单独的成员。而真正的数组指针, 在使用下标或增量时, 会跳过整个数组, 通常只在操作数组的数组时有用—— 如果还有一点用的话。
如果你真的需要声明指向整个数组的指针, 使用类似“int (*ap)[N];” 这样的声明。其中N 是数组的大小。如果数组的大小未知, 原则上可以省略N, 但是这样生成的类型, “指向大小未知的数组的指针”, 毫无用处。
20).当我向一个接受指针的指针的函数传入二维数组的时候, 编译器报错了。
数组蜕化为指针的规则(参见问题6.3) 不能递归应用。数组的数组(即C语言中的二维数组) 蜕化为数组的指针, 而不是指针的指针。数组指针常常令人困惑, 需要小心对待;
如果你向函数传递二位数组:
int array[NROWS][NCOLUMNS];
f(array);
那么函数的声明必须匹配:
void f(int a[][NCOLUMNS])
{ ... }
或者
void f(int (*ap)[NCOLUMNS]) /* ap 是个数组指针*/
{ ... }
在第一个声明中, 编译器进行了通常的从“数组的数组” 到“数组的指针” 的隐式转换; 第二种形式中的指针定义显而易见。因为被调函数并不为数组分配地址, 所以它并不需要知道总的大小, 所以行数NROWS 可以省略。但数组的宽度依然重要, 所以列维度NCOLUMNS (对于三维或多维数组, 相关的维度) 必须保留。
如果一个函数已经定义为接受指针的指针, 那么几乎可以肯定直接向它传入二维数组毫无意义。
当数组是函数的参数时, 为什么sizeof 不能正确报告数组的大小?
编译器把数组参数当作指针对待, 因而报告的时指针的大小。
21).为什么这段代码不行?char *answer; printf("Type something:\n");gets(answer); printf("You typed \"%s\"\n", answer);
指针变量answer, 传入gets(), 意在指向保存得到的应答的位置, 但却没有指向任何合法的位置。换言之, 我们不知道指针answer 指向何处。因为局部变量没有初始化, 通常包含垃圾信息, 所以甚至都不能保证answer 是一个合法的指针。
改正提问程序的最简单方案是使用局部数组, 而不是指针, 让编译器考虑分配的问题:
#include <stdio.h>
#include <string.h>
char answer[100], *p;
printf("Type something:\n");
fgets(answer, sizeof answer, stdin);
if((p = strchr(answer, ’\n’)) != NULL)
*p = ’\0’;
printf("You typed \"%s\"\n", answer);
本例中同时用fgets() 代替gets(), 以便array 的结束符不被改写。不幸的是, 本例中的fgets() 不会象gets() 那样自动地去掉结尾的nn。也可以用malloc() 分配answer 缓冲区。
22).我的strcat() 不行.
我试了char *s1 = "Hello, "; char *s2 ="world!"; char *s3 = strcat(s1, s2); 但是我得到了奇怪的结果。
这里主要的问题是没有正确地为连接结果分配空间。C 没有提供自动管理的字符串类型。C 编译器只为源码中明确提到的对象分配空间(对于字符串, 这包括字符数组和串常量)。程序员必须为字符串连接这样的运行期操作的结果分配足够的空间, 通常可以通过声明数组或调用malloc() 完成。
strcat() 不进行任何分配; 第二个串原样不动地附加在第一个之后。因此, 一种解决办法是把第一个串声明为数组:
char s1[20] = "Hello, ";
由于strcat() 返回第一个参数的值, 本例中为s1,而s3 实际上是多余的; 在strcat() 调用之后, s1 包含结果。提问中的strcat() 调用实际上有两个问题: s1 指向的字符串常数, 除了空间不足以放入连接的字符串之外, 甚至都不一定可写。
23).static的作用
1.static如果是修饰函数或全局变量,该变量会被隐藏,成为Internal linkage的变量,其他文件的代码中无法访问到。
2.static如果在函数中修饰局部变量,将会保持变量内容的持久性,在该函数外依然可以访问到变量的内容。
3.static还有一个作用是被修饰的变量会被初始化为0,和全局变量类似,不同的是函数内部被static修饰的变量也会初始化为0。
24).我的程序总是崩溃, 显然在malloc 内部的某个地方。但是我看不出哪里有问题。是malloc() 有bug 吗?
很不幸, malloc 的内部数据结构很容易被破坏, 而由此引发的问题会十分棘手。最常见的问题来源是向malloc 分配的区域写入比所分配的还多的数据; 一个常见的bug 是用malloc(strlen(s)) 而不是strlen(s) + 1。其它的问题还包括使用指向已经释放了的内存的指针, 释放未从malloc 获得的内存, 或者两次释放同一个指针, 或者试图重分配空指针.
25).为什么在调用free() 之后指针没有变空?使用(赋值, 比较) 释放之后的指针有多么不安全?
当你调用free() 的时候, 传入指针指向的内存被释放, 但调用函数的指针值可能保持不变, 因为C 的按值传参语义意味着被调函数永远不会永久改变参数的值。
严格的讲, 被释放的指针值是无效的, 对它的任何使用, 即使没有解参照, 也可能带来问题, 尽管作为一种实现质量的表现, 多数实现都不会对无伤大雅的无效指针使用产生例外。
2.当我malloc() 为一个函数的局部指针分配内存时, 我还需要用free() 明确的释放吗?
是的。记住指针和它所指向的东西是完全不同的。局部变量在函数返回时就会释放, 但是在指针变量这个问题上, 这表示指针被释放, 而不是它所指向的对象。用malloc() 分配的内存直到你明确释放它之前都会保留在那里。一般地, 对于每一个malloc() 都必须有个对应的free() 调用。
3.我在分配一些结构, 它们包含指向其它动态分配的对象的指针。我在释放结构的时候, 还需要释放每一个下级指针吗?
是的。一般地, 你必须分别向free() 传入malloc() 返回的每一个指针, 仅仅一次(如果它的确要被释放的话)。一个好的经验法则是对于程序中的每一个malloc() 调用, 你都可以找到一个对应的free() 调用以释放malloc() 分配的内存。
26).alloca() 是什么?为什么不提倡使用它?
在调用alloca() 的函数返回的时候, 它分配的内存会自动释放。也就是说, 用alloca 分配的内存在某种程度上局部于函数的“堆栈帧” 或上下文中。alloca() 不具可移植性, 而且在没有传统堆栈的机器上很难实现。当它的返回值直接传入另一个函数时会带来问题, 如fgets(alloca(100), 100, stdin)。由于这些原因, alloca() 不合标准, 不宜使用在必须广泛移植的程序中, 不管它可能多么有用。既然C99 支持变长数组(VLA), 它可以用来更好的完成alloca() 以前的任务。
2.calloc() 和malloc() 有什么区别?利用calloc 的零填充功能安全吗?free() 可以释放calloc() 分配的内存吗, 还是需要一个cfree()?
calloc(m, n) 本质上等价于p = malloc(m * n);memset(p, 0, m * n);
填充的零是全零, 因此不能确保生成有用的空指针值或浮点零值。free() 可以安全地用来释放calloc() 分配的内存。
27).为什么strcat(string, ’!’); 不行?
字符和字符串的区别显而易见, 而strcat() 用于连接字符串。
C 中的字符用它们的字符集值对应的小整数表示。字符串用字符数组表示; 通常你操作的是字符数组的第一个字符的指针。二者永远不能混用。要为一个字符串增加!, 需要使用
strcat(string, "!");
2.我在检查一个字符串是否跟某个值匹配。为什么这样不行?char *string; . . . if(string == "value") { /* string matches ”value”
*/ . . . }
C 中的字符串用字符的数组表示, 而C 语言从来不会把数组作为一个整体操作(赋值, 比较等)。上面代码段中的== 操作符比较的是两个指针—— 指针变量string 的值和字符串常数“value” 的指针值—— 看它们是否相等, 也就是说, 看它们是否指向同一个位置。它们可能并不相等, 所以比较决不会成功。
要比较两个字符串, 一般使用库函数strcmp():
if(strcmp(string, "value") == 0) {
/* string matches "value" */
3.如果我可以写char a[] = "Hello, world!"; 为什么我不能写
char a[14]; a = "Hello, world!";
字符串是数组, 而你不能直接用数组赋值。可以使用strcpy() 代替:
strcpy(a, "Hello, world!");
}
4.我认为我的编译器有问题: 我注意到sizeof(’a’) 是4 而不是1 (即,不是sizeof(char))。
可能有些令人吃惊, C语言中的字符常数是int 型, 因此sizeof(’a’) 是sizeof(int),这是另一个与C++ 不同的地方。
28).因为在C 语言中所有的非零值都被看作“真”, 是不是把TRUE 定义为1 很危险?如果某个内置的函数或关系操作符“返回” 不是1 的其它值怎么办?
C 语言中的确任何非零值都都被看作真, 但这仅限于“输入”, 也就是说, 仅限于需要布尔值的地方。内建操作符生成布尔值时, 可以保证为1 或0。因此, 这样的测试
if((a == b) == TRUE)
能如愿运行(只要TRUE 为1), 但显然这很傻。事实上, 跟TRUE 和FALSE的跟TRUE 和FALSE 的显示比较都不合适, 因为有些库函数(如isupper(),isalpha() 等) 在成功时返回非零值, 但不一定为1。(再说, 如果你认为“if((a == b)== TRUE)” 比“if(a == b)” 好, 为什么就此打住呢?为什么不使用“if(((a == b)== TRUE) == TRUE)” 呢?) 一般规则是只在向布尔变量赋值或函数参数中才使用TRUE 和FALSE (或类似的东西), 或者用于函数的返回值, 但决不用于比较。
预处理宏TRUE 和FALSE (当然还有NULL) 只是用于增加代码可读性, 而不是因为其值可能改变。
尽管使用TRUE 和FALSE 这样的宏(或者YES 和NO) 看上去更清楚, 布尔值和定义在C 语言中的复杂性让很多程序员觉得TRUE 和FALSE 宏不过更令人迷惑, 因而更喜欢使用1 和0。
if语句、while语句中的控制表达式的值也不能是结构体类型。严格来说,可以做算术运算的类型称为算术类型(Arithmetic Type),算术类型包括整型和浮点型。可以表示零和非零,可以参与逻辑与、或、非运算或者做控制表达式的类型称为标量类型(Scalar Type),标量类型包括算术类型和以后要讲的指针类型。
29).书写多语句宏的最好方法是什么?
通常的目标是书写一个象包含一个单独的函数调用语句的宏。这意味着“调用者” 需要提供最终的分号, 而宏体则不需要。因此宏体不能为简单的括弧包围的复合语句, 因为如果这样, 调用的时候就会发生语法错(明显是一个单独语句, 但却多了一个额外的分号), 就像在if/else 语句的if 分支中多了一个else 分句一样。(方便在if中调用宏函数,而不会造成语法错误)
所以, 传统的结局方案就是这样使用:
#define MACRO(arg1, arg2) do { \
/* declarations */ \
stmt1; \
stmt2; \
/* ... */ \
} while(0) /* 没有结尾的; */
当调用者加上分号后, 宏在任何情况下都会扩展为一个单独的语句。优化的编译器会去掉条件为0 的“无效” 测试或分支, 尽管lint 可能会警告。如果宏体内的语句都是简单语句, 没有声明或循环, 那么还有一种技术, 就是写一个单独的, 用一个或多个逗号操作符分隔的表式。
30).我第一次把一个程序分成多个源文件, 我不知道该把什么放到.c 文件, 把什么放到.h 文件。
作为一般规则, 你应该把这些东西放入头(.h) 文件中:
² 宏定义(预处理#defines)
² 结构、联合和枚举声明
² typedef 声明
² 外部函数声明(参见问题1.4)
² 全局变量声明
当声明或定义需要在多个文件中共享时, 尤其需要把它们放入头文件中。特别是, 永远不要把外部函数原型放到.c 文件中。
另一方面, 如果定义或声明为一个.c 文件私有(static), 则最好留在.c 文件中。
相关推荐
“本书是Summit以及C FAQ在线列表的许多参与者多年心血的结晶,是C语言界最为珍贵的财富之一。我向所有C语言程序员推荐本书。” ——Francis Glassborow,著名C/C++专家,ACCU(C/C++用户协会)前主席 “本书清晰...
C语言的一本经典教材,是以FAQ的形式给出495个C语言易出错的例子,很适合学习和面试用。 C是一门简洁精妙的语言,掌握基本语法容易,真正能够自如运用,就不那么简单了。你难免会遇到各种各样的问题,有些可能让你...
标题中的“用C语言编写的一个迷宫程序”指的是一个使用C语言实现的计算机程序,它设计了一个迷宫游戏或者算法。C语言是一种底层、高效且广泛使用的编程语言,适合实现这种逻辑复杂、性能要求高的应用。 迷宫程序...
总的来说,这个压缩包提供了一个使用C语言解决旅行商问题的完整实例,包括源代码、可执行文件和测试数据。对于学习图论、组合优化或者C语言编程的人员来说,这是一个宝贵的资源,有助于深入理解算法设计和实现过程。
在计算机科学领域,资源分配问题是一个重要的议题,尤其是在操作系统设计和优化中占据核心地位。C语言作为一门底层、高效且广泛应用的编程语言,是解决这类问题的理想工具。本主题将深入探讨C语言如何用于实现资源...
《你必须知道的495个经典C语言问题》是一本专门为C语言学习者和开发者编写的指南,它汇聚了全球C语言编程者的集体智慧,旨在解决C语言学习过程中的常见疑惑和挑战。这本书在Amazon上获得了全五星的评价,充分证明了...
"C语言考研复试面试常见问题集合" 本资源总结了C语言考研复试面试中常见的问题,涵盖了声明和初始化、结构、联合和枚举等主题。这些问题都是C语言开发中的热点问题,了解这些问题和答案对C语言开发者的职业发展和...
标题中提到的“495个C语言问题”暗示了这本书包含大量关于C语言的详尽问题及其解答。C语言是一种广泛使用的计算机编程语言,它允许程序员直接与硬件交互,控制底层资源。该语言于1972年由Dennis Ritchie在AT&T的贝尔...
"你必须知道的495个C语言问题完整版本"是一个深度学习C语言的资源,它涵盖了C语言中的各种核心概念、语法、数据类型、控制结构、函数、指针、内存管理等关键知识点。 1. **核心概念**: C语言的基础包括变量声明、...
C语言是一种广泛使用的编程语言,它的语法简洁明了,适合初学者入门,也适用于系统级编程和嵌入式系统开发。本资料集“C语言常见问题集”旨在帮助开发者解决在学习和实践中遇到的各种问题。以下是一些C语言中常见的...
《你必须知道的495个C语言问题》是一本专为C语言学习者和开发者设计的指南,它涵盖了从基础概念到高级特性的广泛问题集合。这本书的高清扫描版300dpi提供了清晰的阅读体验,对于那些喜欢纸质书感觉但又希望方便电子...
《你必须知道的495个C语言问题》是一本深度探讨C语言的书籍,它涵盖了C语言编程中的各种常见问题和陷阱,旨在帮助程序员深入理解C语言的本质,避免编程过程中遇到的诸多难题。该书的完整版本包含了495个精心挑选的...
c语言圣诞树代码C语言设计一个闪闪的圣诞树C语言设计一个闪闪的圣诞树C语言设计一个闪闪的圣诞树C语言设计一个闪闪的圣诞树C语言设计一个闪闪的圣诞树C语言设计一个闪闪的圣诞树C语言设计一个闪闪的圣诞树C语言设计...
《你必须知道的495个C语言问题》是一本深度剖析C语言的参考资料,它旨在帮助读者深入理解和掌握这门编程语言的核心概念与技术。这本书涵盖了从基础语法到高级特性的广泛问题,旨在提高读者在C语言领域的技能水平。 ...
C语言是一种强大的、通用的编程语言,被广泛用于系统编程、应用编程、嵌入式开发等领域。它以其高效、灵活性和简洁的语法而受到程序员的青睐。"495个C语言问题"这个主题涵盖了C语言学习过程中的各种挑战和疑问,旨在...
c语言做的一个任务管理器 c语言做的播放器源码 c语言做的绘图板系统 c语言别踩白块儿(双人版)源码 c语言力学相关的流体源码 c语言华容道源码 c语言吃逗游戏源码 c语言商品销售系统源码 c语言图书借阅系统源码 c语言...
C语言是一种广泛使用的编程语言,由贝尔实验室的Dennis Ritchie在1972年开发。它是面向过程的,语法简洁明了,被广大程序员所喜爱。C语言提供了丰富的控制结构,包括条件语句(if-else)、循环(for、while)、函数...