2.第二题考察宏定义
#define A 2 + 2
#define B 3 + 3
#define C A * B
int main(int argc, char *argv[])
{
printf("%d\n", C);
return 0;
}
C=A*B;此时,C=2+2*3+3=11,而不是4*6
如果在宏定义中给A,B加上括号,则会是另一种结果。
此处还可以去了解#和##在宏中的使用。
#
用来将宏参数转换为字符串,也就是在宏参数的开头和末尾添加引号。
##
称为连接符,用来将宏参数或其他的串连接起来。
6.考察对字符串指针和字符数组的理解。
int main(int argc, char *argv[])
{
char *str = "Xiyou Linux Group";
printf("%c\n", *str + 1);
return 0;
}
字符串中的所有字符在内存中是连续排列的,str 指向的是字符串的第 0 个字符;我们通常将第 0 个字符的地址称为字符串的首地址。字符串中每个字符的类型都是char
,所以 str 的类型也必须是char *
。
*str + 1=(*str )+ 1,*优先级大于+;此时取了X,对其加一。
*(str + 1)=首地址加1再取值(str[1]),因此两次输出不同。
为了加深的理解,请继续阅读下面的代码:
#include <stdio.h>
#include <stdlib.h>
int main(){
char str[20] = {0};
int i;
for(i=0; i<10; i++){
*(str+i) = 97+i; // 97为字符a的ASCII码值
}
printf("%s\n", str);
printf("%s\n", str+2);
printf("%c\n", str[2]);
printf("%c\n", (str+2)[2]);
return 0;
}
运行结果:
abcdefghij cdefghij c e
前面三个 printf() 比较容易理解,str+2 表示指向第 2 个元素,(str+2)[2] 相当于 *(str+2+2),也就是取得第 4 个元素的值。
我们对字符串指针和字符数组进行拓展:
它们都可以使用%s
输出整个字符串,都可以使用*
或[ ]
获取单个字符,这两种表示字符串的方式是不是就没有区别了呢?
有!它们最根本的区别是在内存中的存储区域不一样,字符数组存储在全局数据区或栈区,第二种形式的字符串存储在常量区。全局数据区和栈区的字符串(也包括其他数据)有读取和写入的权限,而常量区的字符串(也包括其他数据)只有读取权限,没有写入权限。
10.
int main(int argc, char *argv[])
{
int a = 1;
printf("%d\n", *(char *)&a);
}
结果是1,这个题本身没什么意义,无非是一个强制转化,我们看下面的代码加深理解。
void print(void *x) /* Pointer Received as void - integer *a=300/
{
char e= *(char *)x; // e=',' Ascii Equivalent of 4 (256+44)
int b= *(int *)x; // b= 300
printf("%c \n",e); // ','
printf("%d",b); //300
}
11.这个题就是对字符串指针和字符数组的考察
int main(int argc, char *argv[])
{
/*const*/ char a[] = "XiyouLinux\0";
char *b = "XiyouLinux\0";
a[5] = '\0';
// b[5] = '\0';
printf("%s\n", a);
// printf("%s\n",b);
return 0;
}
在C语言中,字符串总是以'\0'
作为结尾,所以'\0'
也被称为字符串结束标志,或者字符串结束符。
printf() 输出字符串时,会从第 0 个元素开始往后检索,直到遇见'\0'
才停止,然后把'\0'
前面的字符全部输出,这就是 printf() 输出字符串的原理。
12.一个C源文件到可执行文件
- 1).预处理,产生.ii文件
- 2).编译,产生汇编文件(.s文件)
- 3).汇编,产生目标文件(.o或.obj文件)
- 4).链接,产生可执行文件(.out或.exe文件)
预处理
gcc -E hello.c -o hello.i
- 头文件展开 —不检查语法错误,可以展开任意文件
- 宏定义—将宏名替换为宏值,不检查语法错误
- 替换注释—变成空行
- 展开条件编译—根据条件来展开指令
编译
gcc -S hello.i -o hello.s
- 将c程序翻译为汇编指令
- 逐行检查语法错误–整个编译4步中最耗时间的过程
汇编
gcc -c hello.s -o hello.o
- 将汇编指令翻译成对应的二进制编码
链接
gcc hello.o -o hello.exe
- 数据段合并
- 数据地址回填
- 库引入
c语言:源文件从文本到可执行文件经历的过程_MARSHCW的博客-优快云博客
13.这是一个冒泡排序
void sort(int arr[], int size)
{
int i, j, tmp;
for (i = 0; i < size - 1; i++)
{
for (j = 0; j < size - i - 1; j++)
{
if (arr[j] > arr[j + 1])
{
tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
若在某一趟排序中未发现气泡位置的交换,则说明待排序的无序区中所有气泡均满足轻者在上,重者在下的原则,因此,冒泡排序过程可在此趟排序后终止。为此,在下面给出的算法中,引入一个标签flag,在每趟排序开始前,先将其置为0。若排序过程中发生了交换,则将其置为1。各趟排序结束时检查flag,若未曾发生过交换则终止算法,不再进行下一趟排序。
原文链接:https://blog.youkuaiyun.com/yanxiaolx/article/details/51622286
//冒泡排序优化2
void BubbleSort3(int* arr, size_t size)
{
assert(arr);
int i = 0, j = 0;
int k = size - 1,pos = 0;//pos变量用来标记循环里最后一次交换的位置
for (i = 0; i < size - 1; i++)//一共要排序size-1次
{
//每次遍历标志位都要先置为0,才能判断后面的元素是否发生了交换
int flag = 0;
for (j = 0; j <k; j++)//选出该趟排序的最大值往后移动
{
if (arr[j] > arr[j + 1])
{
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
flag = 1;//只要有发生了交换,flag就置为1
pos = j;//循环里最后一次交换的位置 j赋给pos
}
}
k = pos;
//判断标志位是否为0,如果为0,说明后面的元素已经有序,就直接return
if (flag == 0)
{
return;
}
}
}