本文内容随缘更新,仅仅记录学习期间的重难点

Q:如果i是int型变量,f是float型变量,那么条件表达式(i>0?i:f)是哪一种类型

A:如果int和float混在一起,会使得表达式为float型。如果i>0为真,那么i转化为float型后的值就为表达式的值。


Q:printf打印详解

A:使用printf时怎么控制输出位数和保留小数点位数
值得一提的是,printf的返回值是打印的内容的字节数。


Q:空语句的实例

A:

1
2
3
4
for(d=2;d<n;d++)
if(n%d==0)
break;
for(d=2;d<n&&n%d!=0;d++);


Q:数组的元素个数必须给一个常数吗

A:通常情况下是的,但在C99标准下新增了变长数组(VLA)这一定义

1
2
3
4
5
6
7
8
9
int main()
{
int a;
scanf("%d",&a);
int arr1[a];
//甚至可以这样
int arr2[a+9*5];
int arr3[a][a];//多维数组
}

变长数组的的长度是程序执行时计算的,而不是程序编译的时候计算的。因此,他们不具有静态存储期
变长数组不具备初始化器,因此,不能对其进行初始化处理。

值得注意的是,虽然变长数组是C99标准规定的,但其在VScode(MinGW),VS2019和VS2022上均无法实现(Linux上的gcc可实现),因此可移植性较差,应尽量避免使用

另外的,换行符,空格符和制表符会让scanf停止录入,这一点值得注意。


Q:

1
2
3
4
5
6
7
8
9
10
int main()
{
int a[10],sum,*p;
...//对a的元素赋值
sum=0;
for(p=&a[0];p<&a[10];p++)
{
sum+=*p;
}
}

&a[10]不会产生未定义行为吗

A:尽管a[10]不存在,但是对它使用取地址运算符是合法的。因为循环不会尝试检查a[10]的值,因此非常安全,同时应该指出,改用下标很容易写出不使用指针的循环,并且由于不同的实现方式,对于有些IDE来说,使用下标的写法可能是更好的代码,换句话说,可以节省更多的执行时间。


Q:字面串有哪些重点
A:就本质而言,C语言把字面串当作字符数组处理,当C语言编译器在程序中遇到长度为n的字面串时,它会为字面串分配长度为n+1的内存空间,这块内存空间将用来存储字面串中的字符,以及\0。既然是作为数组存储的,那么编译器会把它看作char\*类型的指针,例如printf函数都接收char*类型的值作为它们的第一个值

1
printf("abc");

在这个例子中printf实际上接收的就是字符a内存单元中的指针
并且abc作为数组,不难想出可以有以下操作
1
2
3
4
char *p;
p="abc";
char ch;
ch="abc"[1];//ch='b'


Q:字符串的复制
A:在通常情况下我们会使用以下方法进行复制

1
2
3
4
#include<string.h>
char s1[5];
char s2[]="abcd";
strcpy(s1,s2);

这里再介绍一个比较少用的C语言库函数strncpy
1
2
#include<string.h>
strncpy(s1,s2,sizeof(s1));//其中第三个参数可以用于限制所复制的字符数

只要s1足够装下存储在s2中的字符串(包括空字符),复制就能完成,当然,strncpy本身也不是没有风险。如果s2中存储的字符串的长度大于s1,strncpy会导致s1的字符串没有终止的空字符。
下面我将给出一种更安全的用法
1
2
3
4
5
#include<string.h>
char s1[5];
char s2="abcdef";
strncpy(s1,s2,sizeof(s1-1));
s1[sizeof(s1)-1]='\0';


Q:sizeof和strlen函数在不同环境下的辨析
A:下面会通过举例来展示

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
int main()
{
int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a));//sizeof(a)表示计算整个数组大小,16
printf("%d\n", sizeof(a + 0));//sizeof(a+0)计算的是首元素地址,32位是4,64位是8
printf("%d\n", sizeof(*a));//*a是首元素,所以sizeof(*a)是4
printf("%d\n", sizeof(a + 1));//同2,答案是4
printf("%d\n", sizeof(a[1]));//4
printf("%d\n", sizeof(&a));//4
printf("%d\n", sizeof(*&a));//16,同1
printf("%d\n", sizeof(&a + 1));//4/8,跳过整个数组,但仍是地址
printf("%d\n", sizeof(&a[0]));//4/8
printf("%d\n", sizeof(&a[0] + 1));//第二个元素的地址因此是4/8
return 0;
}
int main()
{
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", sizeof(arr));//6
printf("%d\n", sizeof(arr + 0));//4/8
printf("%d\n", sizeof(*arr));//1
printf("%d\n", sizeof(arr[1]));//1
printf("%d\n", sizeof(&arr));//4/8
printf("%d\n", sizeof(&arr + 1));//4/8
printf("%d\n", sizeof(&arr[0] + 1));//4/8
}
int main()
{
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", strlen(arr));//随机值,因为要碰见\0
printf("%d\n", strlen(arr+0));//随机值
printf("%d\n", strlen(*arr));//error把97作为地址放入strlen
printf("%d\n", strlen(arr[1]));//error
printf("%d\n", strlen(&arr));//随机值
printf("%d\n", strlen(&arr+1));//随机值-6
printf("%d\n", strlen(&arr[0]+1));//随机值-1
}

int main()
{
char arr[] = "abcdef";
printf("%d\n", sizeof(arr));//7,还有斜杠0
printf("%d\n", sizeof(arr + 0));//4/8
printf("%d\n", sizeof(*arr));//1
printf("%d\n", sizeof(arr[1]));//1
printf("%d\n", sizeof(&arr));//4/8
printf("%d\n", sizeof(&arr + 1));//4/8
printf("%d\n", sizeof(&arr[0] + 1));//4/8
}

int main()
{
char arr[] = "abcdef";
printf("%d\n", strlen(arr));//6,到\0结束
printf("%d\n", strlen(arr+0));//6
printf("%d\n", strlen(*arr));//error把97作为地址放入strlen
printf("%d\n", strlen(arr[1]));//error
printf("%d\n", strlen(&arr));//6 warning &arr的类型是char(*)[7]
printf("%d\n", strlen(&arr+1));//随机值 warning
printf("%d\n", strlen(&arr[0]+1));//5
}

int main()
{
char* p = "abcdef";
printf("%d\n", sizeof(p));//计算指针变量p的大小 4 a的地址
printf("%d\n", sizeof(p+1));//4/8 b的地址
printf("%d\n", sizeof(*p));//1 字符a的大小
printf("%d\n", sizeof(p[0]));//1 arr[0]==*(arr+0) p[0]==*(p+0)
printf("%d\n", sizeof(&p));//4/8
printf("%d\n", sizeof(&p+1));//4/8
printf("%d\n", sizeof(&p[0]+1));//4/8
}


int main()
{
char* p = "abcdef";
printf("%d\n", strlen(p));//6
printf("%d\n", strlen(p + 1));//5
printf("%d\n", strlen(*p));//error
printf("%d\n", strlen(p[0]));//error
printf("%d\n", strlen(&p));//随机值
printf("%d\n", strlen(&p + 1));//随机值
printf("%d\n", strlen(&p[0] + 1));//5

}


int main()
{
int a[3][4] = { 0 };
printf("%d\n", sizeof(a));//4*12=48
printf("%d\n", sizeof(a[0][0]));//4
printf("%d\n", sizeof(a[0]));//16
printf("%d\n", sizeof(a[0] + 1));//4/8,第一行的一维数组的第二个a[0][1]的地址
printf("%d\n", sizeof(*(a[0] + 1)));//4,int的大小
printf("%d\n", sizeof(a + 1));//4/8是指针大小,是第二行首元素的指针
printf("%d\n", sizeof(*(a + 1)));//是整个第二行的大小,所以是16
printf("%d\n", sizeof(&a[0] + 1));//4/8
printf("%d\n", sizeof(*(&a[0] + 1)));//16,第二行的大小
printf("%d\n", sizeof(*a));//16,计算第一行的大小
printf("%d\n", sizeof(a[3]));//16,sizeof不会访问,不会去验证是否错误
}