C语言常见问题总结

1.用/* */注释代码在c语言中不是好办法,尤其是在注释的代码中原先已经有注释存在,可能会报错,更好的办法是用#if 和 #endif;

2.如果有一些声明需要用于几个不同的源文件,可以在一个单独的文件编写这些声明,然后用#include命令把这个文件包含到需要使用这些声明的源文件中;

3.标准的C编译器不会对数组下标的有效性进行检查,如果存储数据超出数组长度就会存储在紧随数组之后的内存位置,这会破坏原有的存储在这个位置的数据,导致多种结果;

4.NULL指针是一个特殊的指针变量,表示不指向任何东西,它可以赋值给一个指针,用于表示那个指针不指向任何值。对一个NULL指针进行解引用操作是非法的,引起的后果因编译器而异,两个常见的后果分别是返回内存位置零的值和终止程序。

5.在使用指针变量之前,需要对其显示的初始化,如果知道指针将被初始化为什么地址,就将他初初始化为什么地址,否则将其初始化为NULL。

6.C语言中,不能通过检查一个值的位来判断它的类型,值的类型并非值本身所固有的一种特性,而是取决于他的使用方式。

7.结构体变量是一个标量,它可以用于其它标量可以使用的任意场合,因此可以把结构体作为参数传递给一个函数,但是往往不这么做,这样做效率很低,因为C语言的参数传值方式要求把参数的一份拷贝传递给函数,这样这个结构将占用大量的空间。通常的做法是给函数传递一个指向结构的指针,指针比整个结构要小的多。但是向函数传递指针的缺陷在于函数现在可以对调用程序的结构变量进行修改,如果不希望如此,可以在函数中使用const关键字来防止这类修改。

8.编译器为一个结构变量的成员分配内存时要满足他们边界对齐的要求。在实现结构存储的边界对齐时,可能会浪费一部分内存空间。根据边界对齐要求降序排列结构成员可以最大限度的减少结构存储中浪费的内存空间。sizeof返回的值包含了结构中浪费的内存空间。

9.malloc

malloc函数返回的是一块向内存池申请的连续的内存,当内存池为空的,malloc会返回一个NULL指针,因此对每个从malloc返回的指针进行检查,确保非NULL是非常重要的。malloc返回是void *的指针,因此一个void*可以转换为其他任何类型的指针,但有的编译器需要强制转换。

10.函数只能返回标量值,不能返回数组。

11.所有用于数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时,由于参数中的操作符或邻近的操作符之间不可预料的相互作用。

#define DOUBLE(X) ((X)*(X))

12.C语言封装接口注意事项

提供接口,函数参数除了原生类型,其它的全部传指针。

不要对外暴露锁,尽量不要让调用者手工分配内存。

内存管理,需要谁申请就让谁释放,如果你的接口内部申请了内存供外部使用那么一定要提供接口让外部调用去释放内存(因为内存管理的库会存在差异)。

所有的数组必须带长度。

所有的函数调用应该有返回值,所有的错误有清晰的描述。

windows下保存好pdb。

接口中必须有一个查询版本号的接口。

不对历史接口做扩展,而是添加新接口。

最后就是写一个接口手册。

13.C语言中如何定义字符串?

可以通过字符数组或字符指针来定义字符串,也可以用宏定义对常量字符串进行定义。

char str1[] = "HelloWorld";  // 通过字符数组来定义字符串"HelloWorld",数组中每个存储单元存放一个字符

char *str2  = "HelloWorld";  // 通过字符指针来定义字符串"HelloWorld",指针str2指向一个存放字符串"HelloWorld"的连续地址单元的首地址

#define str3 "HelloWorld";  // 通过宏定义来定义字符串"HelloWorld",等价于str3="HelloWorld"

14.C语言结构体边界对齐问题

情况一:没有#pragma pack宏的情况

边界对齐遵循的三个原则:

原则1、普通数据成员对齐规则:第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。

原则2、结构体成员对齐规则:如果一个结构里有某些结构体成员,则该结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储。)

原则3、结构体大小对齐规则:结构体大小也就是sizeof的结果,必须是其内部成员中最大的对齐参数的整数倍,不足的要补齐。

情况二:如果有#pragma pack宏,对齐方式按照宏的定义来

#pragma pack规定的对齐长度,实际使用的规则是:

结构,联合,或者类的数据成员,第一个放在偏移为0的地方,以后每个数据成员的对齐,按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。

也就是说,当#pragma pack的值等于或超过所有数据成员长度的时候,这个值的大小将不产生任何效果。

而结构整体的对齐,则按照结构体中最大的数据成员 和 #pragma pack指定值 之间,较小的那个进行。

15.字节序问题

主机字节序:

不同的CPU有不同的字节序类型,这些字节序是指整数在内存中保存的顺序,这个叫做主机序。最常见的有两种 1.Big endian:低字节存高地址,高字节存低地址 2.Little endian:低字节存低地址,高字节存高地址

网络字节序:

网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。网络字节顺序采用big endian排序方式。

注:通过对大小端的存储原理分析可发现,对于 char 型数据,由于其只占一个字节,所以不存在这个问题,这也是一般情况下把数据缓冲区定义成 char 类型 的原因之一。

Linux 系统为大小端模式的转换提供了 4 个函数,输入 man byteorder 命令可得函数原型:

#include <arpa/inet.h> 

uint32_t htonl(uint32_t hostlong); 

uint16_t htons(uint16_t hostshort); 

uint32_t ntohl(uint32_t netlong); 

uint16_t ntohs(uint16_t netshort);

s是short

l 是long

h是host

n是network

htonl 表示 host to network long ,用于将主机 unsigned int 型数据转换成网络字节顺序; 

htons 表示 host to network short ,用于将主机 unsigned short 型数据转换成网络字节顺序;

ntohs 把unsigned short类型从网络序转换到主机序

ntohl 把unsigned long类型从网络序转换到主机序。

16.INADDR_ANY 转换过来就是0.0.0.0,泛指本机的意思,也就是表示本机的所有IP,因为有些机子不止一块网卡,多网卡的情况下,这个就表示所有网卡ip地址的意思。 比如一台电脑有3块网卡,分别连接三个网络,那么这台电脑就有3个ip地址了,如果某个应用程序需要监听某个端口,那他要监听哪个网卡地址的端口呢? 如果绑定某个具体的ip地址,你只能监听你所设置的ip地址所在的网卡的端口,其它两块网卡无法监听端口,如果我需要三个网卡都监听,那就需要绑定3个ip,也就等于需要管理3个套接字进行数据交换,这样很繁琐, 所以出现INADDR_ANY,你只需绑定INADDR_ANY,管理一个套接字就行,不管数据是从哪个网卡过来的,只要是绑定的端口号过来的数据,都可以接收到。

17.TCP粘包

发生TCP粘包、拆包主要原因:

  1. 应用程序写入的数据大于套接字缓冲区大小,这将会发生拆包。
  2. 应用程序写入数据小于套接字缓冲区大小,网卡将应用多次写入的数据发送到网络上,这将会发生粘包。
  3. 进行mss(最大报文长度)大小的TCP分段,当TCP报文长度-TCP头部长度>mss的时候将发生拆包。
  4. 接收方法不及时读取套接字缓冲区数据,这将发生粘包。

如何解决拆包粘包:

既然知道了tcp是无界的数据流,且协议本身无法避免粘包,拆包的发生,那我们只能在应用层数据协议上,加以控制。通常在制定传输数据时,可以使用如下方法:

  1. 使用带消息头的协议、消息头存储消息开始标识及消息长度信息,服务端获取消息头的时候解析出消息长度,然后向后读取该长度的内容。
  2. 设置定长消息,服务端每次读取既定长度的内容作为一条完整消息。
  3. 设置消息边界,服务端从网络流中按消息编辑分离出消息内容。

a)先基于第三种方法,假设区分数据边界的标识为换行符"\n"(注意请求数据本身内部不能包含换行符),数据格式为Json,例如下面是一个符合这个规则的请求包。

  1. {"type":"message","content":"hello"}\n  

注意上面的请求数据末尾有一个换行字符(在PHP中用双引号字符串"\n"表示),代表一个请求的结束。

b)基于第一种方法,可以制定,首部固定10个字节长度用来保存整个数据包长度,位数不够补0的数据协议

  1. 0000000036{"type":"message","content":"hello"}  

c)基于第一种方法,可以制定,首部4字节网络字节序unsigned int,标记整个包的长度

  1. ****{"type":"message","content":"hello all"}  

其中首部四字节*号代表一个网络字节序的unsigned int数据,为不可见字符,紧接着是Json的数据格式的包体数据。

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值