1.大小和长度竟然不是一个意思, sizeof()
和strlen()
有什么异同之处? 他们对于不同参数的结果有什么不同?请试举例子说明。
int main(void)
{
char s[] = "I love Linux\0\0\0";
int a = sizeof(s);
int b = strlen(s);
printf("%d %d\n", a, b);
}
输出:16 12
sizeof和strlen的异同:
不同点: | 相同点: |
---|---|
sizeof是运算符,strlen是函数 | 两者都可以计算字符串数组中元素的个数 |
sizeof计算所含空间字节数(注意不要忘记'\0') !!小tips:sizeof括号里只能运算,不能计算;strlen用于计算字符长度,遇'\0'停止,且不包含'\0' | |
sizeof在编译时就计算出结果,strlen在运行时才会计算出结果 |
2.箱子的大小和装入物品的顺序有关,test1
和test2
都含有:1个short
、1个int
、1个double
,那么sizeof(t1)
和sizeof(t2)
是否相等呢?这是为什么呢?
struct test1 {
int a;
short b;
double c;
};
truct test2 {
short b;
int a;
double c;
};
int main(void)
{
struct test1 t1;
struct test2 t2;
printf("sizeof (t1) : %d\n", sizeof(t1));
printf("sizeof(t2): %d\n", sizeof(t2));
}
输出:16 16
-
主要考察结构体内存对齐
首先是内存对齐的三个原则: |
---|
1.结构体变量的首地址,必须是结构体变量中的“最大基本数据类型成员所占字节数”的整数倍。 |
2.结构体变量中的每个成员相对于结构体首地址的偏移量,都是该成员基本数据类型所占字节数的整数倍 |
3.结构体变量的总大小,为结构体变量中 “最大基本数据类型成员所占字节数”的整数倍 |
回到本题中,t1为例,int占4个字节,short占2个字节,4是2的整数倍所以不用补齐,double占8个字节,前面已经有6个字节被占了,6不是8的整数倍,所以前面要补2个字节,所以double前面是8个字节,加上double本身的8个字节,一共16个字节!16个字节刚好是t1最大数据结构类型(double)的整数倍,所以整体不用补齐,所以最后答案就是16啦!
t2同理,自己试试吧
3. 哦,又是函数> 想必在高数老师的教导下大家十分熟悉函数这个概念。那么你了解计算机程序设计中的函数吗?请编写一个func
函数,用来输出二维数组arr
中每个元素的值。
/*在这里补全func函数的定义*/
int main(void)
{
int arr[10][13];
for (int i = 0; i < 10; i++){
for (int j = 0; j < 13; j++) {
arr[i][j] = rand();
}
}
func(arr,10,13);
}
void func(int arr[][13],int m,int n)//没有返回值,函数类型为void
{
int i,j;
for(i=0;i<m;i++){
for(j=0;j<n;j++){
printf("%d",a[i][j]);
}
}
}
4.就不能换个变量名吗?请结合下面的程序,简要谈谈传值
和传址
的区别。简要谈谈你对C语言中变量的生命周期的认识。
int ver = 123;
void func1(int ver)
{
ver++;
printf("ver = %d\n", ver);//1026
}
void func2(int *pr)
{
*pr = 1234;
printf("*pr = %d\n", *pr); //1234
pr = 5678; //error,不能将int型常量赋给地址
printf("ver = %d\n", ver);//123
}
int main()
{
int a = 0;
int ver = 1025;
for (int a = 3; a < 4; a++)
{
static int a = 5;
printf("a = %d\n", a); //5
a = ver;
func1(ver);
int ver = 7;
printf("ver = %d\n", ver); // 7
func2(&ver);
}
printf("a = %d\tver = %d\n", a, ver);//0 1025
}
传值和传址的区别:
传值:
1.不改变变量,相当于产生了一个一模一样的形参,造成压栈。浪费空间大,并减低效率。
2.传参无法改变原来变量。
传址:
1.可以改变原来变量。
2.地址不开辟新的空间,节省空间。
全局变量和局部变量:
变量分为局部与全局,局部变量又可称之为内部变量。 由某对象或某个函数所创建的变量通常都是局部变量,只能被内部引用,而无法被其它对象或函数引用。 全局变量既可以是某对象函数创建,也可以是在本程序任何地方创建。
划重点!!
局部变量的生命周期和作用域都是在大括号——块内的,块里定义了和外面同名的变量则会掩盖外面的同名变量,并且不能在一个块内定义同名的变量
5.套娃真好玩!请说明下面的程序是如何完成求和的?
unsigned sum(unsigned n) //无符号整形
{
return n ? sum(n - 1) + n : 0;
}
int main(void)
{
printf("%u\n", sum(100));
}
//求和100+99+98+...+1的表达式
表达式1?表达式2:表达式3 解释:若表达式1成立,则执行表达式2,否则执行表达式3
6.算不对的算术
void func(void)
{
short a = -2;
unsigned int b = 1;
b += a;
int c = -1;
unsigned short d = c * 256;
c <<= 4;
int e = 2;
e = ~e | 6;
d = (d & 0xff) + 0x2022;
printf("a=0x%hx\tb=0x%x\td=0x%hx\te=0x%x\n", a, b, d, e);
//a=0xfffe,b=0xffffffff,d=0x2022,e=0xffffffff
printf("c=Ox%hhx\t\n", (signed char)c);
//c=0xf0
}
1."|" 或运算符 有1为1,全0为0. |
---|
2."&" 与运算符 有0为0,全1为1. |
3.“^" 异或运算符 相同为0,不同为1. |
4.“<<" 左移运算符 可以理解为去掉最左边的数,在最右边补0,往左移一位 |
5.“>>" 右移运算符 可以理解为去掉最右边的数,在最左边补0,往右移一位 |
6.“~” 非运算符 相当于取反 |
7.“%#X” #表示完整呈现所有数值的位数,x表示16进制 |
7.指针和数组的恩怨情仇
int main(void)
{
int a[3][3] = {
{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int(*b)[3] = a; //行指针,指向数组的指针
++b; //指向后一行
b[1][1] = 10; //指向最后一行
int *ptr = (int *)(&a + 1); //刚开始指向第一行,+1后指向下一行
printf("%d %d %d \n", a[2][1], **(a + 1), *(ptr - 1));//10 4 9
}
8.移形换位之术。
下面有a
、b
、c
三个变量和4个相似的函数。
你能说出使用这三个变量的值或地址作为参数分别调用这5个函数,在语法上是否正确吗? 请找出下面的代码中的错误。
const int
和int const
是否有区别?如果有区别,请谈谈他们的区别。
const int *和
int const *`是否有区别?如果有区别,请谈谈他们的区别。
int a = 1;
int const b = 2;
const int c = 3;
void funco(int n)
{
n += 1;
n = a;
}
void func1(int *n)
{
*n += 1;
n = &a;
}
void func2(const int *n)//此函数有错,const修饰指针,表示指针指向的值不能被改变
{
*n += 1;//不能被赋值
n = &a;
}
void func3(int *const n)//有错,const修饰地址,表明指针的地址不能被改变
{
*n += 1;
n = &a;//地址不能被改变
}
void func4(const int *const n)//指针指向的值和指针的地址都不能被改变
{
*n += 1; //不能被赋值
n = &a;//地址不能被改变
}
1.const int和 int const都表示int类型的变量不能被修改 |
---|
2.const int和int const都表示指针指向的值不能被修改 |
[const总是修饰离他后面最近的变量名或数据类型]
-
听说翻转字母大小写不影响英文的阅读?
请编写
convert
函数用来将作为参数的字符串中的大写字母转换为小写字母,将小写字母转换为大写字母。返回转换完成得到的新字符串。char *convert(const char *s); int main(void) { char *str = "XiyouLinux Group 2022"; char *temp = convert(str); puts(temp); }
char *convert(const char *s) { int i; char *p=(char*)malloc(sizeof(char)*strlen(s)); //动态分配空间 p[21]='\0';//增加终止符 strcat(p,s);//拼接 for(i=0;i<21;i++){ if(p[i]>='A'&&p[i]<='Z'){ p[i]=p[i]+'a'-'A'; }else if(p[i]>='a'&&p[i]<='z'){ p[i]=p[i]-'a'+'A'; } } free(p);//释放空间 return p; }
10.交换礼物的方式
请判断下面的三种
Swap
的正误,分别分析他们的优缺点。你知道这里的do {...} while(0)
的作用吗?你还有其他的方式实现Swap
功能吗?#define Swap1(a, b, t) do { t = a; a = b; b = t; } while (0)//正确 #define Swap2(a, b) do { int t = a; a = b; b = t; } while (0)//正确 void Swap3(int a, int b) { int t = a; a = b; b = t; }//错误,只进行了值传递 //形参在函数调用完之后自动释放空间,main函数里并不会发生交换
do{ }while()的作用是为了在宏定义中实现局部作用域 用大括号括起来表示是一个整体,是swap里语句,不然可能发生错误 用大括号也能实现但是在预处理被替换时可能会有格式问题,例如:
#define Swap2(a, b) { int t = a; a = b; b = t; } int main(void) { int a,b; scanf("%d %d",&a,&b); if(a>b){ swap(a,b); } } //在预处理时被替换后 int main(void) { int a,b; scanf("%d %d",&a,&b); if(a>b){ int t = a; a = b; b = t; };//swap后会出现分号,编译通不过 }
11.据说有个东西叫参数
你知道
argc
和argv
的含义吗?请解释下面的程序。你能在不使用argc
的前提下,完成对argv
的遍历吗?int main(int argc, char *argv[]) { printf("argc = %d\n", argc); for (int i = 0; i < argc; i++) printf("%s\n", argv[i]); }
1.argc和argv中的arg是参数,其中argc是int类型的整数,用于统计运行程序时送给main函数的命令行参数的个数 2.*argv[]中为输入的字符串内容,它是一个指向字符串的指针 3.argc中总是存在一个参数即程序全名,这也是为什么argc的值为1的原因 4.不会出现死循环,因为当argc加到一定值后会发生溢出,会变为负数 #include<stdio.h> int main(void) { int i=0; while(argv[i]!=NULL){ printf("%s ",argv[i++]); } return 0; }
12.人去楼空
这段代码有是否存在错误?谈一谈静态变量与其他变量的异同。
int *func1(void) { static int n = 0; //静态变量的生命周期从开始到本函数结束 n = 1; return &n; //函数形参在函数结束时自动释放,但如果声明为static则值一直存在空间不会被释放 } int *func2(void) { int *p = (int *)malloc(sizeof(int)); //动态分配空间 *p = 3; return p; } int *func3(void) { int n = 4; return &n;//不能返回局部变量地址这是很危险的 } int main(void) { *func1() = 4; *func2() = 5; *func3() = 6;
静态变量的作用:
-
隐藏与隔离的作用
-
保持内容的持久性
-
默认初始化为0
13.奇怪的输出
int main(void) { int data[] = {0x636c6557, 0x20656d6f, 0x78206f74,0x756f7969, 0x6e694c20, 0x67207875,0x70756f72, 0x32303220, 0x00000a31}; puts((const char*)data); }
输出:Welcome to xiyou linux group 2021
详见【无标题】西邮Linux兴趣小组2022纳新试题_一定.的博客-CSDN博客第12题
14.请谈谈对从「C语言文件到可执行文件」的过程的理解
-
1.预处理:在此阶段,头文件的展开(#include); - 宏替换(#define); - 条件编译的处理(#if #endif等); - 去注释; 将.c文件变为预处理后的.i临时文件 |
---|
2.编译: 词法分析; - 语义分析; - 语法分析; - 符号汇总; 将对每个.i文件单独转化为.s文件 |
3.汇编: 形成符号表; 将.s的汇编文件变为.o的目标文件 |
4.链接:合并段表; - 合并符号表和符号表的重定位; 把每个目标文件.o合并成为一个可执行程序(Linux默认是a.out文件) |
详见谈谈C语言从源文件变为可执行文件之间发生的过程详解(C程序的编译链接运行)_呋喃吖的博客-CSDN博客_如何把c语言源程序变成应用程序