# 错误
errno 是 error number 的缩写,意味系统调用错误码。
在 C 语言中,errno 是一个全局变量,用于表示系统调用或库函数返回的错误代码。当系统调用或库函数失败时,errno 会被设置为相应的错误代码。可以使用 perror () 函数来打印出 errno 的值和对应的错误信息。
例如,在使用 open () 函数打开文件时,如果文件不存在,则会返回 - 1, 并将 errno 设置为 ENOENT。可以使用以下代码来检查 errno 的值并输出错误信息:
#include <stdio.h> | |
#include <errno.h> | |
int main() { | |
FILE *file = fopen("nonexistent_file.txt", "r"); | |
if (file == NULL) { | |
perror("Error opening file"); | |
printf("errno: %d\n", errno); | |
} else { | |
// do something with the file | |
fclose(file); | |
} | |
return 0; | |
} |
在这个例子中,如果文件不存在,perror () 函数会输出 "Error opening file", 并且 printf () 函数会输出 errno 的值和对应的错误信息。
# 断言
断言 (assertion) 是一种在程序中的一阶逻辑 (如:一个结果为真或假的逻辑判断式),目的为了表示与验证软件开发者预期的结果 —— 当程序执行到断言的位置时,对应的断言应该为真。若断言不为真时,程序会中止执行,并给出错误信息。
在 C 语言中,断言被分为静态断言和运行时断言。静态断言可以在编译时生效,如果不满足断言的要求,则会编译失败;运行时断言是程序在运行时的逻辑判断,但判断条件不为真时,程序报错退出。
# 静态断言
通过 static_assert
这个宏可以实现静态断言,程序在编译时,会判断宏函数第一个参数是否为 0 (即为假),如果为 0 ,则程序终止编译,同时打印第二个参数指定的信息。
比如下面这个例子,如果断言 sizeof(a) <= 8
,则程序无法编译通过。
#include<stdio.h> | |
#include<stdlib.h> | |
#include <assert.h> | |
int main() { | |
//a = b; | |
int arr[4]; | |
static_assert(sizeof(arr)<=8,"This arr too big!"); | |
printf("OK!"); | |
while (1) { | |
} | |
} |
如果断言 sizeof(a) > 8
,在我所用的平台上,这个值应为 16,大于 8 ,所以静态断言的判断条件为真,程序编译通过。
# 运行时断言
下面是运行时断言的简单例子,如果不把 p = &a;
屏蔽,指针 p 不为空,程序能顺利往下运行;反之如果将 p=&a;
屏蔽,程序会在断言处退出。
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int main(void)
{
int number = 9;
int* p = NULL;
//p = &number;
// 断言 p 不为空
assert(p != NULL);
printf("ok!\n");
return 0;
}
解除注释 p = &number; 后正常执行
assert 不能和 static_assert 那样自定义错误信息。
另外,C 语言程序运行时断言还受宏常量 NDEBUG 影响,如果该宏常量先于 #include <assert.h>
定义时,编译器会忽略 assert 部分代码。这样我们就能灵活控制断言的开启与关闭。
# 对齐
字节对齐是 C 语言中的一种机制,用于在内存中存储数据时,使数据的起始地址是其数据类型大小的整数倍。这样做可以提高 CPU 访问内存的效率。例如,如果一个变量的内存地址正好位于它长度的整数倍,那么这个变量就被称做自然对齐。
在 C 语言中,可以使用__align () 函数或__attribute__((aligned ())) 来指定结构体成员或变量的对齐方式。例如,下面的代码将结构体的成员按照 8 字节对齐:
struct my_struct { | |
char a; | |
int b; | |
short c; | |
char d[10]; | |
} attribute__((aligned(8))); |