C语言基础

本文最后更新于 2024年12月1日 上午

1、内建函数

1.1 概念

内建函数,顾名思义,就是编译器内部实现的函数。这些函数跟关键字一样,可以直接使用,无须像标准库函数那样,要 #include 对应的头文件才能使用。

内建函数的函数命名,通常以 __builtin 开头。这些函数主要在编译器内部使用,主要是为编译器服务的。内建函数的主要用途如下。

  • 用来处理变长参数列表;
  • 用来处理程序运行异常;
  • 程序的编译优化、性能优化;
  • 查看函数运行中的底层信息、堆栈信息等;
  • C 标准库函数的内建版本。

1.2 常用内建函数

1、__builtin_return_address(LEVEL)

这个函数用来返回当前函数或调用者的返回地址。函数的参数 LEVEl 表示函数调用链中的不同层次的函数,各个值代表的意义如下。

  • 0:返回当前函数的返回地址;
  • 1:返回当前函数调用者的返回地址;
  • 2:返回当前函数调用者的调用者的返回地址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <stdio.h>

void f(void) {
int *p;
p = __builtin_return_address(0);
printf("f return address: %p\n", p);
p = __builtin_return_address(1);;
printf("func return address: %p\n", p);
p = __builtin_return_address(2);;
printf("main return address: %p\n", p);
printf("\n");
}

void func(void) {
int *p;
p = __builtin_return_address(0);
printf("func return address: %p\n", p);
p = __builtin_return_address(1);;
printf("main return address: %p\n", p);
printf("\n");
f();
}

int main(void) {
int *p;
p = __builtin_return_address(0);
printf("main return address: %p\n", p);
printf("\n");
func();
printf("goodbye!\n");
return 0;
}

2、 C 标准库的内建函数

在 GNU C 编译器内部,实现了一些和 C 标准库函数类似的内建函数。这些函数跟 C 标准库函数功能相似,函数名也相同,只是在前面加了一个前缀 __builtin。如果你不想使用 C 库函数,也可以加个前缀,直接使用对应的内建函数。

常见的标准库函数如下:

  • 内存相关的函数:memcpy 、memset、memcmp

  • 数学函数:log、cos、abs、exp

  • 字符串处理函数:strcat、strcmp、strcpy、strlen

  • 打印函数:printf、scanf、putchar、puts

1
2
3
4
5
6
7
8
#include <stdio.h>

int main(void) {
char a[100];
__builtin_memcpy(a, "hello world!", 20);
printf("%s", a);
return 0;
}

2、数据类型

原码、补码和反码是计算机对整数的二进制表示方法

三种表示方法均由符号位和数值位构成:其中符号位是二进制序列的第一个数字(0表示正数,1表示负数,数值位对于正数来说原码、反码和补码相同,负数的原码、反码和补码不同,计算规则如下:

  • 原码:直接将数字转化为二级制序列并加上符号位就是原码(正负数相同)

  • 反码:负数的反码是符号位不变,其他位置按位取反(即1变成0,0变成1)

  • 补码:反码+1就是补码

    数值 原码 反码 补码
    5 00000101 00000101 00000101
    -5 10000101 11111010 11111011

3、数组与指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdio.h>

int main(void) {

int arr1[2][3] = {1, 2, 3, 4, 5, 6};
int arr2[6] = {1, 2, 3, 4, 5, 6};

int *p = arr2;

int (*p1)[2][3] = arr1;
int (*p2)[3] = arr1[0];
int *p3 = &arr1[0][0];

printf("%d\n", *(p2 + 1)[0]);

printf("%d\n", *p + 3);
printf("%d %d\n", arr1, p1);
printf("%d %d\n", arr1[0], arr1[1]);
printf("%d %d\n", &arr1[0][0], &arr1[0][1]);


printf("%d %d\n", *p1[0][0], p1);
printf("%d %d\n", arr1[0], arr1[1]);
printf("%d %d\n", &arr1[0][0], &arr1[0][1]);

printf("%d\n", arr1);
printf("%d %d\n", arr1[0], arr1[1]);
printf("%d %d\n", &arr1[0][0], &arr1[0][1]);

return 0;
}

4、c语言不定参数用法

4.1、不定参数的函数定义和使用到的c函数

(1)定义

1
2
3
void fun1(参数类型 argName,...);
示例:
void fun1(int count,...);

(2)获取不定参数的值

1
2
3
4
5
6
#include <stdarg.h> //包含头文件

va_list vaList;//定义结构体变量
va_start(vaList,count);//开始遍历不定参数
va_arg(vaList,type);//获取一个不定参数的值,type:int整数、double小数、char*字符串
va_end(vaList);//结束遍历

4.2、不定参数函数示例

1)计算int类型数据总和,计算结果为8,argName的值不是使用va_arg函数获取的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
#include <stdarg.h>

int madd(int argName, ...);

int main() {
printf("%d", madd(3, 1, 2, 5));
return 0;
}

int madd(const int argName, ...) {
int tempNum = 0;

va_list vaList;
va_start(vaList, argName);

int i;
for (i = 0; i < argName; i++) {
tempNum += va_arg(vaList, int);
}

va_end(vaList);

return tempNum;
}

(2)字符串格式化拼接,输出100-21.34%-testdfs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <stdio.h>
#include <stdarg.h>
#include <string.h>

void mprintf(const char *format,...);

int main()
{
mprintf("%d-%.2f%-%sdfs\n",100,21.345,"test");
return 0;
}

void mprintf(const char *format,...)
{
int fmtPos = 0;//原始字符串format的字符位置
char tempStr[32];//拼接好不定参数的新字符串
memset(tempStr,0,sizeof(tempStr));//清空数组
int tempPos=0;//新字符串tempStr的字符位置

va_list vaList;
va_start(vaList,format);

for(fmtPos=0;fmtPos<strlen(format);fmtPos++){//遍历原始字符串
if(format[fmtPos] == '%' && format[fmtPos+1] == 'd'){//格式化输入整数
sprintf(tempStr,"%s%d",tempStr,va_arg(vaList,int));
tempPos = strlen(tempStr);
fmtPos++;
}else if(format[fmtPos] == '%' && format[fmtPos+1] == 's'){//格式化输入字符串
sprintf(tempStr,"%s%s",tempStr,va_arg(vaList,char*));
tempPos = strlen(tempStr);
fmtPos++;
}else if(format[fmtPos] == '%' && format[fmtPos+1] == 'f'){//格式化输入小数,小数点后全部保留
sprintf(tempStr,"%s%lf",tempStr,va_arg(vaList,double));
tempPos = strlen(tempStr);
fmtPos++;
}else if(format[fmtPos] == '%' && format[fmtPos+1] == '.' && format[fmtPos+3] == 'f'){//格式化输入小数,小数后进行保留指定位
switch(format[fmtPos+2]){//默认6位,选择1-5位保留
case '1':
sprintf(tempStr,"%s%.1lf",tempStr,va_arg(vaList,double));
tempPos = strlen(tempStr);
fmtPos+=3;
break;
case '2':
sprintf(tempStr,"%s%.2lf",tempStr,va_arg(vaList,double));
tempPos = strlen(tempStr);
fmtPos+=3;
break;
case '3':
sprintf(tempStr,"%s%.3lf",tempStr,va_arg(vaList,double));
tempPos = strlen(tempStr);
fmtPos+=3;
break;
case '4':
sprintf(tempStr,"%s%.4lf",tempStr,va_arg(vaList,double));
tempPos = strlen(tempStr);
fmtPos+=3;
break;
case '5':
sprintf(tempStr,"%s%.5lf",tempStr,va_arg(vaList,double));
tempPos = strlen(tempStr);
fmtPos+=3;
break;
default:
sprintf(tempStr,"%s%lf",tempStr,va_arg(vaList,double));
tempPos = strlen(tempStr);
fmtPos+=3;
break;
}
}else{
tempStr[tempPos] = format[fmtPos];
tempPos++;
}
}

va_end(vaList);
printf("%s",tempStr);
return;
}

优秀文档:https://blog.csdn.net/weixin_58808338/article/details/142990438

本文作者: zhangJinLong
本文链接: https://zhang426fly.github.io/2025/07/05/c/cyuyan基础/
版权声明: 本博客所有文章除特别声明外,均采用BY-NC-SA许可协议。转载请注明出处!


C语言基础
https://zhang426fly.github.io/2025/07/05/c/cyuyan基础/
作者
zhangJinLong
发布于
2025年7月5日
许可协议