Pointers on C reading

1. /ddd 8进制

2./xddd 16进制

? 需要 /? 而 !则不用

宽字符常量就是在字符前面加上L,表示拿来当unicode使用,即将每个字符变成一个字来存储。

9.2

if(strlength(x) >= 10)…    和

if(strlength(x) – 10 > 0)

结果是不一样的,因为strlength返回的是size_t类型的无符变量。

9.3

strcpy是会把null复制过去的,所以某种角度看,被复制的那个字符串null后面的字符是无法访问的,也就是说丢失了的。
而strncpy则不会以NULL结尾。
但是strncat则会加上NULL结尾。

strcat还有个问题,就是原字符串必须以null结尾。

strcat(char *dst, char *src)

一定要注意,字符串无论是用于str开头的函数,还是printf等指令,都必须要以null结尾。

9.5

strchr
strrchr
前面是第一次出现,后面是最后一次出现

strpbrk
查找任何一组字符第一次在字符串中出现的位置

strstr查找子串:
当子串为空,就返回s1

9.6 高级字符串查找

strspn(char const *str, char const *group)

strcspn(char const *str, char const *group)

前者是指从字符串起始位置开始,包含的group中元素的个数。如果group包含空格, 制表这样的空字符,那么将返回str左边空白字符的个数。
后者是相反,返回字符串起始部分不与group字符匹配的字符。
这个也是会改变str到断点前一个字符。

strtok

image 

第一个参数非null,则保存断点,如果第一个参数为null,则从断点后开始寻找下一个断点。
保存断点就是指将源字符串在断点处截断,填入null,这样实际上就改变了源字符串。

strtok不能嵌套使用,也叫不可重入。(函数在连续几次调用中,即使参数相同结果也可能不同。)

9.8

字符操作包括一些判断字符的函数,我不知道的有

iscntrl, isxdigital, isalnum, ispnct(可打印的符号), isgraph, isprint(可打印的,包括图形和空白)

因为str的操作遇到null会停止,所以要用mem类得操作。

memmove和memcpy差不多,但是它的源地址和目标地址可以相同。

memcmp只能用来单字节单字节的比较

memchr寻找ch第一次出现的地方。

10.结构和联合体

struct tag {member-list} variable-list;

所有可选部分至少要出现两个

如果两次出现struct那么就是错误,合适的方法是用tag变量,先使用struct tag,然后再用struct tag来定义别的变量。也可以用typedef struct {}成为一个类型,再用类型来定义别的变量。

箭头描述符内建了间接访问,不需要显式的执行间接访问或使用括号。
而且是直接找到该结构的体的成员。
->的优先级高于&
比如若要pi指向结构体ex中的a,

Ex x;
Ex *px = &x;

int *pi;
pi = (int *)px; //这样不安全
pi = &px->a; //这样比较安全

也就是说->和地址没有直接关系。只是一种间接访问的符号,就其就可以找到对应的结构体,然后根据成员的类型(指针or非指针)来用->或者.来进行访问。

结构体不能自应用,但是可以应用结构体的指针。注意下面这个陷阱:

typedef struct {
    int a;
    SELF_REF3 *b;
    int c;
} SELF_REF3
这样是会失败,因为知道生命结尾定义才生效。应该这样:
typedef struct SELF_REF3_TAG{
 int a;
   struct SELF_REF3_TAG *b;
   int c;
} SELF_REF3

两个结构体互相包含对方类型的指针,如何解决问题,应该使用不完整声明。

做左值就必须存在于某个地方,常常所说的不是合法的左值就是说这个指针不知道存在何处。

标量是相对于数组而言的。

如果想知道结构体某个成员的实际位置:
offset(Type, member)。

image

传递结构参数是合法的,但是只能通过return返回,而且有两次拷贝,不如传指针方便。

结构体的位段,只能是int, uint或者sint类型。
位段在访问某个寄存器的时候特别方便,如果不用位段就要用位操作来执行,比如或与之类的。
但是位段的移植性不好。

image 

位操作:
与,MASK,或

关于联合

image

image

关于结构体和联合的总结:

image 

11. 动态内存分配

image

malloc的参数是size_t类型。

calloc 和 calloc的区别是后者将分配的空间初始化为0。realloc如果分配比原来小的内存就把原来那部分后面的内存拿走,如果拿不走,就会重新分配一块内存,把原来的拷贝过来。如果realloc的第一个参数是NULL,那就和malloc一样。

image

第12章:使用结构和指针

链表-单链表-双链表

插入排序的三种做法:
1. 普通的,保存current和previous还有new三个指针
2. 传入rootp指针的地址的,保存rootp,current,previous三个指针
3. 上面两种做法对root节点要区别对待,实际上所有的共同点就是都是指向当前节点的link字段的指针,还有指向当前节点的指针。两个指针。
最终版本:
sll_insert(register Node **linkp, int new_value)
{
    register Node *current;
    register Node *new;

    while( ( current = *linkp ) != NULL &&
        current->value < new_value)
            linkp = &current->link;

     new = (Node *)malloc(sizeof(Node));
     if (new == NULL)
           return FALSE;
     new->value  = new_value;

     new->link = current;
     *linkp = new;
     return TRUE;
}

概括if语句块的方法:
1. 语句提炼(statement factoring),把if后两种不同情况下相同的句子提取出来。但是会影响if判断的句子则不能提取。
2. 利用if中的==代码块进行变量替换,从而可以进一步化简。

双链表插入最后实现方法:

image

image

使用sizeof增强可移植性。

13. 指针的高级用法

多个[]或者()或者*在一起的时候,如何分析

首先看先和谁结合,[]或者()结合的优先级比*高,这样就决定了是什么东西,然后再根据其他东西决定这个东西的定语。就像拨皮一样一层层分析。
与*结合是指针,与()结合是函数,先定型,再加定,一层一层剥开来。

image

注意上面的话,函数名在使用的时候编译器总是将其转化为指针,而&只是显示的说明了编译器将隐式执行的操作。

image

三种方法调用效果相同。

image

注意上面说的,函数参数设置为void *,然后在具体的函数中间使用(int *)之类的将其转换为所需要的类型。有时候可以将strcmp作为compare函数传入,但是有的编译器会报错,因为strcmp参数的类型是(char *)而并不是(void *)。

image

转移表的概念。书中说害怕指针走到不该走的地方去,是不是这样就可以解决这个问题:
double (*oper_func[])(double, double) = {
        add, sub, mul, div, …, NULL
};

static int do_req_ack(char *data, int socket);
static int do_sync_term(char *data, int socket);
static int do_test_term(char *data, int socket);
static int do_data_process(char *data, int socket);

static struct action_dispatch dispatch_table[] = {
        { WMD_TERM_ACT_REQ, FRAME_REQ_BYTES, do_req_ack },
        { WMD_TERM_ACT_SYNC, FRAME_SYNC_BYTES, do_sync_term },
        { WMD_TERM_ACT_TEST, FRAME_TEST_BYTES, do_test_term },
        { WMD_TERM_ACT_WATER, FRAME_WATER_BYTES, do_data_process },
        { 0, 0, NULL }
};

补充:我就使用过类似的代码,只是不知道还有个叫转换表的,这个实际上可以做如下转换:
static int (*act_dispatch[])(char *data, int socket) = {
    req, sync, test, data, NULL
}
然后enum { REQ, ….}, 调用的时候act_dispatch[REQ]。

13. 4 命令行参数

argc 参数表示参数的个数,*argv表示参数列表。第一个参数是程序名。

image

image

如果是一个横杠后面带多个选项,就用这个方法:

image

读取所有选项。

但是我觉得书中的上面那个Main在最后的时候不能再用*argv来表示文件名了,因为argv已经在循环中被破坏了。

但是即使这样仍然没有解决如果选项后面还要跟需要分析的单词,或者选项本身就是单词的情况。可以参照toolbox或者busybox中具体程序的实现方法。

NOTICE: malloc分配的内存可以直接返回

NOTICE: 字符串实际上就是个指针

“xyz” + 1      *”xyz”   “xyz”[n] 这些实际上都是有意义的。

image

image

binary to ascii 的两种方法

image

image

14. 预处理

__FILE__  __LINE__ __DATA__ __TIME__ __STDC__
如果遵循ANSI C, __STDC__就为1.

#define CASE break;case

#define 如果特别长就需要除了最后一行之外,每行结尾都加上反斜杠。

#define 宏不要乱加分号。

宏的各个成员都要加上括号,不然就会:

#define SQUARE X*X 如果x = a+1, 则 a+1*a+1…

所以应该为(x)*(x) ((x)+(x))

image

image

image

宏和函数比有哪些优点:

image

宏的副作用举例:

image

image

宏的命名约定:

让人们能够轻松的分辨宏和函数,默认的约定,是将宏名大写。

image

字面值常量/符号常量

image

image

在define中直接用printf

image

#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol

image

头文件的优势:

image

头文件include的写法:
1. 实际上都可以写成<>,但是如果该写成“”而没有写,就会浪费查找时间。

2.

image

头文件嵌套

image

#error宏的用法:
#error error mesg

#line number “string”
number会修改下一行输入的行号__LINE__的值,而string会修改__FILE__的值。

单个的#也被称为无效指令。#progma一般是打开一些编译选项。

image

15。 输入输出函数

15.1 错误报告

image

exit是void类型的。

对标准函数库的修改方式:

image

如何及时的输出打印。

image

流分成文本流和二进制流。文本流在标准中是以换行符结束,和unix下是一样的,但是在mdos中是以回车+换行结束的。标准库负责在不同的标准间切换。

EOF FOPEN_MAX FILENAME_MAX

流实际上是FILE指针

image

而这个是讲述getchar/putchar/gets/puts/scanf/printf等io函数的。

image

 

IO宏与函数的比较:

image

关于unget

image

image

fgets所需要的缓冲区最少要2个字节,其中一个是null。另外gets最多只读缓冲区减一个字节。而fputs则一直打印到null.

image

gets 只适用与玩具程序,它不要求缓冲区大小。

image

scanf 后面的格式字符可以是星号,表示丢弃字符。。。还可以跟宽度,限定符,然后还有普通的格式码。
限定符比如是h,l,L之类的,而格式码可能是d, i, n,   o, u, x,   e, f, g.

image

fscanf跳过空白字符,同时换行符也当空白字符一样跳过。遇到任何非法字符就停止。fgets是遇到回车停止,和fscanf一样,fgets会把换行符换成null加字符串后。
和scanf则读入到空白符就停止,如果读入的是%c那就不加null,如果是%s,就加null。

image

上图是scanf使用说明。

对于文件逐行读入的最优做法是,先用fgets(buffer, BUFFER_SIZE, input)获取行io,然后再用sscanf获取要得到的值。

sprintf也会以null作为结尾。
sprintf出现的问题如何预防:
image

printf的格式代码和格式标识:
对于printf宽度指定最小字符数,精度指定最大字符数。

image

image

printf格式码修改符

image

注意lu有时候是必须的。

除了上面这些,还有格式码修改符。

image

15.11 二进制I/O

二进制I/O的效率最高。

size_t fread(void *buffer, size_t size, size_t count, FILE *stream)
fwrite

其中size是每个成员的大小,而count是总共多少个成员。

C支持随机访问I/O.

ftell可以返回现在的位置。SEEK_SET(从0开始多少个字节)

fseek的副作用:
image

rewind:返回流起始位置

fgetpos/fsetpos fpos_t最好不要分析直接传给同类函数用。

单个程序可以用FILE *tmpfile(void)来创建临时文件,这个文件属性是wb+,但是这个文件不能给别人使用。tmpnam可以用来生成临时文件的名字。

remove(char const *filename)  可以和fopen配合使用。

rename(char const *oldname, char const *newname)

image

image

16. 标准库函数

image

image

image

image

image

image

image

image 

1970-1-1-00:00

最大保存时间应该是2038年,32位整数。

image

前者实际上是image

image

gm代表UTC早前的称谓格林威治标准时间Greenwich Mean Time

image

setjmp和longjmp的用法:

value  = setjmp(restart);

switch(setjmp(restart)) {

    case 1 :

    case 2 :

}

用处:可以省去中间函数出错检测的时间。而且注意,Longjmp和setjump只能在同级别的函数中间或者说Longjmp只能在setjmp同级别或者调用setjmp的函数所调用的函数中使用,不然可能保存的跳转信息会在条用setjmp的函数退出的时候无效了。

除了SIGTERM和SIGINT两个是异步的外,其余基本都是同步的信号。而SIGINT常配有信号处理函数,而SIGTERM则没有。

image

以前的处理函数?

image

因为异步信号处理函数触发的时候不是由abort()或者raise()触发的,所以上下文处在不稳定的状态。

信号处理函数中间的变量尽量用volatile变量。

信号的返回和再次触发:

image

中止执行:

atexit(void(func)(void))注册exit时调用的函数,当exit被调用的时候,所有at函数将按照注册的反顺序被调用,所有流被刷新,打开文件被关闭,tmpfile被删除,然后返回状态。
注意atexit中不能再调用exit。

void assert(int expression)
当expression为假就打印东西出来。
若要消除断言的打印,或者编译的时候将上选项-DNDEBUG,或者在assert.h中加入#define NDEBUG.

char *setlocale(int Category, char const *locale)
本地化的函数,包括数值,字符串,货币的格式。

strcmp在对照序列特殊的情况下,可以用strxcoll来根据对照序列来比较,也可以先通过strxfrm来转化之后再用strcmp比较。

image

tm结构中的月份实际是0-11,指的是1月份之后的月数目。

17 ADT

堆栈一般要求支持三个操作

push pop top 最后一个返回栈顶数据,但是不改变栈的状态

队列可以用循环数组来表示。
其中判断空和满的方法如下所叙:
当队列中有一个元素的时候,rear和front应该都指向它,所以当队列为空的时候,rear应该减1,也就是说队列为空的判断方法是(rear + 1) % QUEUE_SIZE = front. 而在此状态之前应该停止插入函数,所以队列为满的判断方法是(rear + 2) % QUEUE_SIZE = front.要注意,这时候这个队列能最大容纳的数量实际为QUEUE_SIZE –1 。

二叉树如何删除有两个孩子的节点。

数组来实现二叉树时,如果不考虑下标为0的节点,则N双亲为N/2,N左为2N,N右为2N+1.
但是如果考虑下标为0的节点的话,N双亲为(N+1)/2 – 1, N左为2N+1, N右为2N+2.

P378中二叉树的插入实现中,同样利用的是取指向节点的指针的指针来对根节点和子节点同样对待的。而且感觉它只能解决插入发生在叶节点的情况。如果插入不是在叶节点会怎样?

image

 

18。 运行时环境 ? 题目是什么意思

函数分成三个部分:函数序,函数体,函数跋

C标准要求函数能够支持的变量名字至少6个字符

寄存器变量有优势,但是也有劣势,如下所述:

image

为什么函数传入参数压栈是按照参数的反方向进行的?

image

说是因为在反序方案中,额外的参数会被传到前面几个参数的下面,这是为什么?这里面的额外传参有点像stdarg.h中的变长参数一样。

帧指针,有点像在子函数栈空间间接寻址的间址一样。将旧的帧指针入栈,并将堆栈指针赋给帧指针,这是进入子函数开始的工作,最后的时候,将帧指针赋回给栈指针,并将旧的帧指针出栈重新赋值回去。

P398上面汇编中pea a50是什么意思?

image

在汇编层面上微妙影响程序,导致有时候这个错误的程序能返回正确的值。

浅谈如何优化程序:

image

 

image

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值