C专家编程读书笔记
C专家编程读书笔记 第三章 声明
1
const int * grape
int const * grage
两者含义相同,均表示指针所指的对象是只读的
int * const grape
表示指针是只读的
2
? a function can't return a function, so you'll never see foo()()
? a function can't return an array, so you'll never see foo()[]
? an array can't hold a function, so you'll never see foo[]()
You can have any of these:
? a function returning a pointer to a function is allowed: int (* fun())();
? a function returning a pointer to an array is allowed: int (* foo())[]
? an array holding pointers to functions is allowed: int (*foo[])()
3 struct的通常形式
struct 结构标签(可选)
{
类型1 标识符1;
类型2 标识符2;
...
类型N 标识符N;
}变量定义(可选);
4 "parameters are passed to a called function by pushing them on the stack from right to left." XXX wrong
Parameters are passed in registers (for speed) where possible.
5 结构内部有一个指向结构自身的指针,常用于列表list、树tree以及其他动态数据结构
/* struct that points to the next struct */
struct node_tag { int datum;
struct node_tag *next;
};
struct node_tag a,b;
a.next = &b;
6 union的一般结构
union optional_tag {
type_1 identifier_1;
type_2 identifier_2;
...
type_N identifier_N;
} optional_variable_definitions;
7 联合的一个好处,将同一个数据解释为不同类型
union bits32_tag {
int whole; /* one 32-bit value
*/
struct {char c0,c1,c2,c3;} byte; /* four 8-bit bytes
*/
} value;
8 枚举的一般格式
enum optional_tag {stuff... } optional_variable_definitions;
9 The Precedence Rule for Understanding C Declarations
A Declarations are read by starting with the name and then reading in precedence order.
B The precedence, from high to low, is:
B.1 parentheses grouping together parts of a declaration
B.2 the postfix operators:
parentheses () indicating a function, and
square brackets [] indicating an array.
B.3 the prefix operator: the asterisk denoting "pointer to".
C If a const and/or volatile keyword is next to a type specifier (e.g. int, long, etc.) it
applies to the type specifier. Otherwise the const and/or volatile keyword applies to the
pointer asterisk on its immediate left.
10 char* const *(*next)();的含义
"next is a pointer to a function returning a pointer to a const pointer-to-char"
11 typedef
void (*signal(int sig, void (*func)(int)) ) (int);
typedef void (*ptr_to_func) (int);
/* this says that ptr_to_func is a pointer to a function
* that takes an int argument, and returns void
*/
ptr_to_func signal(int, ptr_to_func);
/* this says that signal is a function that takes
* two arguments, an int and a ptr_to_func, and
* returns a ptr_to_func
*/
12 还是typedef
typedef int *ptr, (*fun)(), arr[5];
/* ptr is the type "pointer to int"
* fun is the type "pointer to a function returning int"
* arr is the type "array of 5 ints"
*/
13 typedef和define的区别
1)不可以扩展
#define peach int
unsigned peach i; /* works fine */
typedef int banana;
unsigned banana i; /* Bzzzt! illegal */
2)连续变量声明类型相同
#define int_ptr int *
int_ptr chalk, cheese;
等于
int * chalk, cheese; 变量类型不同
typedef char * char_ptr;
char_ptr Bentley, Rolls_Royce; 变量类型相同
C专家编程读书笔记 第四章 数组和指针
1 区分定义和声明
声明相当于普通的声明:它所说明的并非自身,而是描述其他地
方的创建的对象。
定义相当于特殊的声明:它为对象分配内存。
2 声明和定义应相匹配
char p[10];
extern char *p;
错误!
char *p;
extern p[];
不会出错,但是不推荐。
3 指针和数组的比较
指针
1)指针保存数据的地址
2)间接访问数据,首先取得指针的内容,把它作为地址,然后从这个地址提取数据。如果有下标[i],就把指针的内容加上i作为地址,从中提取数据。
3)通常用于动态数据结构。
4)相关函数为malloc()和free()
5)通常指向匿名数据
数组
1)保存数据
2)直接访问数据,a[i]只是简单的以a+i为地址取得数据
3)通常用于固定数目且数据类型相同的元素
4)隐式分配和删除
5)自身即为数据名
4 指针和数组的初始化
A pointer definition does not allocate space for what's
pointed at, only for the pointer, except when assigned a literal string.
第五章 链接
1 Five Special Secrets of Linking with Libraries
1)Dynamic libraries are called lib something .so , and static libraries are called lib something .a
2)You tell the compiler to link with, for example,libthread.so by giving the option -lthread
3)The compiler expects to find the libraries in certain directories
4)Identify your libraries by looking at the header files you have used
5)Symbols from static libraries are extracted in a more restricted way than symbols from dynamic libraries
2 Where to Put Library Options
Always put the -l library options at the rightmost end of your compilation command line.
3 Moral: Don't make any symbols in your program global, unless they are meant to be part of your interface!
第六章 运行时的数据结构
1 文件中的三个段
文本段(text) 通常是指用来存放程序执行代码的一块内存区域。又叫RO段
数据读(data) 通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。又叫RW段
bss段 通常是指用来存放程序中未初始化的全局变量和静态变量的一块内存区域。又叫ZI段
2 关于BSS段
BSS段的全称叫Block Started by Symbol,可惜根据英文完全看不懂是什么意思,一个更好记的名称为Better Save Space
3 关于段
1)数据段保存在目标文件中
2)BSS段不保存在目标文件中(除了记录BSS段在运行时所需要的大小)
3)文本段是最容易受到优化措施影响的段
4)a.out文件的大小受调试状态下编译的影响,但段不受影响
? the data segment is kept in the object file
? the BSS segment isn't kept in the object file (except for a note of its runtime size requirements)
? the text segment is the one most affected by optimization
? the a.out file size is affected by compiling for debugging, but the segments are not.
4 堆栈的作用
1)堆栈为函数内部声明的局部变量提供存储空间
2)进行函数调用时,堆栈存储于此有关的一些维护性信息
3)堆栈被用作暂时存储区
? The stack provides the storage area for local variables declared inside functions. These are
known as "automatic variables" in C terminology.
? The stack stores the "housekeeping" information involved when a function call is made. This
housekeeping information is known as a stack frame or, more generally, a procedure
activation record. We'll describe it in detail a little later, but for now be aware that it includes
the address from which the function was called (i.e., where to jump back to when the called
function is finished), any parameters that won't fit into registers, and saved values of registers.
? The stack also works as a scratch-pad area—every time the program needs some temporary
storage, perhaps to evaluate a lengthy arithmetic expression, it can push partial results onto
the stack, popping them when needed. Storage obtained by the alloca() call is also on
the stack. Don't use alloca() to get memory that you want to outlive the routine that
allocates it. (It will be overwritten by the next function call.)
5 编译器针对堆栈的优化方法
1)不储存未使用的信息
2)将信息保存于寄存器而不是堆栈中
3)简单的函数调用,不将整个活动过程录入栈
4)让被调用函数而不是调用者负责寄存器的保存工作
第七章 对内存的思考
1 cache
Write-through cache— This always initiates a write to main memory at the same time it writes to the cache.
Write-back cache— In the first instance, this writes only to cache. The data is transferred to main memory when the cache line is about to be written again and a save hasn't taken place yet. It will also be transferred on a context switch to a different process or the kernel.
2 heap
The heap area is for dynamically allocated storage, that is, storage obtained through malloc (memory allocate) and accessed through a pointer. Everything in the heap is anonymous—you cannot access it directly by name, only indirectly through a pointer. The malloc (and friends: calloc, realloc, etc.) library call is the only way to obtain storage from the heap.
malloc and free— get memory from heap and give it back to heap
brk and sbrk— adjust the size of the data segment to an absolute value/by an increment
3 导致段错误的原因
1)dereferencing a pointer that doesn't contain a valid value
2)dereferencing a null pointer (often because the null pointer was returned from routine, and used without checking)
3)accessing something without the correct permission—for example, attempting into a read-only text segment would cause this error
4)running out of stack or heap space (virtual memory is huge but not infinite)
第八章 类型
1 整型提升
the "integral promotions" require that the abstract machine promote the value of each variable to int size and then add the two ints and truncate the sum
Original Type Usually Promoted To
char int
bit-field int
enum int
unsigned char int
short int
unsigned short int
float double
array pointer
of anything to anything
ps.函数在传递参数时可能会发生隐式的类型提升
2 与原型有关的类型提升
#include
int main()
{
union
{
double d;
float f;
} u;
u.d = 10.0;
printf("double in float out = %f \n",u.f);
u.f = 10.0;
printf("float in double out = %d \n",u.d);
return 0;
}
double in float out = 0.000000
float in double out = 1092616192
这段代码的trick在于double使用8byte存放而float用4byte存放。
3 针对函数的强制类型转换
int intcompare(const int *i, const int *j)
{
return(*i - *j);
}
(int (*)(const void *, const void *)) intcompare
第九章 数组
1 数组的声明有三种形式
? declaration of an external array
? definition of an array (remember, a definition is just a special case of a declaration; it allocates space and may provide an initial value)
? declaration of a function parameter
注意!其中第三种可以改写成指针形式,它们是对等的。
也就是说作为函数的参数
char s[]和char *s等同,其他时候不可认为相等。
2 When Arrays Are Pointers?
Rule 1.
An array name in an expression(表达式) (in contrast with a declaration) is treated by the compiler as a pointer to the first element of the array
eg:
int a[10], *p, i=2;
p = a;
a[i] == p[i] == *(p+1)
p = a+1;
a[i] == *p
Rule 2.
A subscript is always equivalent to an offset from a pointer .
Rule 3.
An array name in the declaration of a function parameter is treated by the compiler as a pointer to the first element of the array.
一种理解方法:除了数组和函数之外所有的C语言参数在缺省情况下都是传值调用,数组和函数则是传值调用。数据用取地址的方式也可以传址调用。
3 关于下标的步长
编译器自动把下标值的步长调整到数组元素的大小。对起始地址执行加法操作之前,编译器负责计算每次增加的步长。这也是指针需要有类型的原因。
The compiler automatically scales a subscript to the size of the object pointed at. If integers are 4 bytes long, then a[i+1] is actually 4 bytes (not 1) further on from a[i]. The compiler takes care of scaling before adding in the base address. This is the reason why pointers are always typed— constrained to point to objects of only one type—so that the compiler knows how many bytes to retrieve on a pointer dereference, and it knows by how much to scale a subscript.
4 数组和指针可交换性的总结
1. An array access a[i] is always "rewritten" or interpreted by the compiler as a pointer access *(a+i);
2. Pointers are always just pointers; they are never rewritten to arrays. You can apply a subscript to a pointer; you typically do this when the pointer is a function argument, and you know that you will be passing an array in.
3. An array declaration in the specific context (only) of a function parameter can equally be written as a pointer. An array that is a function argument (i.e., in a call to the function) is always changed, by the compiler, to a pointer to the start of the array.
4. Therefore, you have the choice for defining a function parameter which is an array, either as an array or as a pointer. Whichever way you define it, you actually get a pointer inside the function.
5. In all other cases, definitions should match declarations. If you defined it as an array, your extern declaration should be an array. And likewise for a pointer.
6 a[i,j,k]是合法的,因为逗号表达式取右值,等于a[k]
7 二维数组carrot[i][j]的解析形式为 *(carrot + i) + j)
注意:i和j的跳跃步长不同,i一次跳一行,j一次跳一个。
8 如果数组的长度比初始化长度要长,那么后面的数据会自动置零。
9 只有字符串常量才可以初始化指针数组。指针数组不能由非字符串的类型直接初始化,如果想初始化,可用如下方法:
int row_1[] = {1,2,3,4,5,-1}; /* -1 is end-of-row marker */
int row_2[] = {6,7,-1};
int row_3[] = {8,9,10,-1};
int *weights[] = {
row_1,
row_2,
row_3
};
10 最后留个问题
#include
char ga[]="abcdefghi";
void pri1(char ca[])
{
printf("%x,%x,%x,%d\n",&ca,&(ca[0]),&(ca[1]),sizeof(ca));
}
void pri2(char *pa)
{
printf("%x,%x,%x,%d\n",&pa,&(pa[0]),&(pa[1]),sizeof(pa));
}
void pri3(char *ppa)
{
pri2(ppa);
}
int main()
{
int *p;
p = &ga;
printf("%x,%x,%x,%d\n",p,&(ga[0]),&(ga[1]),sizeof(ga));
printf("%x,%x,%x,%d\n",&p,&(ga[0]),&(ga[1]),sizeof(ga));
pri1(ga);
pri2(ga);
pri3(ga);
return 0;
}
结果是什么?欢迎讨论
第十章 指针
1 指针数组和数组指针
int (*a)[12] 一个指针,指向一个长度12的int数组,指针+1后跳过的长度为12*4
int *a[12] 一个数组,包含十二个int型指针
2 字符串指针的使用方法
eg
char * turnip[UMPTEEN];
char my_string[] = "your message here";
/* share the string */
turnip[i] = &my_string[0];
/* copy the string */
turnip[j] = malloc( strlen(my_string)+ 1 );
strcpy(turnip[j], my_string);
3 函数的实参和所匹配的形式参数
实参 所匹配的形式参数
数组的数组 char c[8][10]; char (*)[10]; 数组指针
指针数组 char *c[15]; char **c; 指针的指针
数组指针(行指针) char (*c)[64]; char (*c)[64]; 不改变
指针的指针 char **c; char **c; 不改变
之所以在main()函数中有char **argv这样的参数,实际上argv是一个指针数组(即char *argv[])。
4 没有办法向函数传递一个普通的多维数组
向函数传递多维数组,除了最左边一维可以省略,其他必须提供具体长度。
eg
invert_in_place( int a[][3][5] );
正确
int b[10][3][5]; invert_in_place( b );
int c[999][3][5]; invert_in_place( c );
错误
int fails1[10][5][5]; invert_in_place( fails1 );/* does NOT compile */
int fails2[999][3][6]; invert_in_place( fails2 );/* does NOT compile *
5 向函数传递多维数组的方法
eg 传递一个二维数组
1) my_function( int my_array [10][20] );
这种方法固定了数组的大小。
2) my_function( int my_array [][20] );
这种方法还是需要保证后维长度。
同样的方法:my_function( int (* my_array)[20] );
3) my_function( char **my_array );
这种方法完全抛弃了二维数组,实际相当于char *my_array[],需要把二维数组改为指向向量的指针
4) char_array[ row_size *i+j]=...
这种方法同样抛弃了二维数组,使用了一维数组下标计算代替。
6 使用指针从函数返回一个数组
int (*paf())[20]
{
int (*pera)[20];
pera = calloc(20,sizeof(int));
return pear;
}
paf是一个函数,它返回一个指向包含20个int元素的数组的指针。
调用方法:
int (*result)[20];
resule = paf();
(*resule)[3] = 12;
9