1. 请试着解释其输出
int main(int argc , char *argv[]) {
unsigned char a = 255;
char ch = 128;
a -= ch;
printf("a = %d ch = %d\n", a, ch);
}
结果:a = 127 ch = -128
`对a:先将a看作int,就是 0000 0000 0000 0000 0000 0000 1111 1111 ,然后截取char类型的长度,就是1111 1111(补码),因为无符号,原码:1111 1111,就是255,减去128得127。
对ch:char类型,8字节,128的原码就是1000 0000,这是个有符号型,所以输出,为-128.
2. 下面代码的运行输出结果是什么,并说说你的理解。
int main(int argc, char *argv[]) {
char *str = "Xi You Linux Group 20";
printf("%d\n", printf(str));
return 0;
}
结果:Xi You Linux Group 2021
printf("%d",printf(str)), 是输出printf(str)的返回值,printf(str)的返回值是printf(str)中输出的字节数,"Xi You Linux Group 20"正好21个字节,所以输出了21,。
3. 这段代码的输出结果是什么?为什么会出现这样的结果?
int i = 2;
void func() {
if(i != 0) {
static int m = 0;
int n = 0;
n++;
m++;
printf("m = %d, n = %d\n", m, n);
i--;
func();
}else{
return;
}
}
int main(){
func();
return 0;
}
这一题相同的类型,我以前的面试题分析写过了,就不过多写了,就是变量的生命周期和定义域的东西。
4. 下面程序会出现什么结果?为什么会出现这样的结果?
int main(int argc, char * argv[]) {
char ch = 'A';
int i = 65;
unsigned int f = 33554433;
*(int *)&f >>= 24;
*(int *)&f = *(int *)&f + '?';
printf("ch = %c i = %c f = %c\n", ch, i, *(int *)&f);
return 0;
}
对ch,ch直接输出A.
对i,A的ascii正好是65
对f,33554433>>24后为2,2+‘?’,?的ascii为63,加起来正好65;
这里的f强制换为int类型后在运算,输出,因为char是有符号型的.
5. 下面代码的运行输出结果是什么,并说说你的理解.
int main(int argc, char *argv[]) {
int a[2][2];
printf("&a = %p \t&a[0] = %p \t&a[0][0] = %p \n", &a, &a[0],&a[0][0]);
printf("&a+1 = %p \t&a[0]+1 = %p \t&a[0][0]+1= %p \n", &a+1,&a[0]+1, &a[0][0]+1);
return 0;
}
&a,%a[0],&a[0][0]指向的地址一样,都是数组的一个数的地址。
&a+1是在&a[0][0]的地址上增加sizeof(a)的长度,&a[0]+1是在&a[0][0]的地址上增加sizeof(a[0])的长度,&a[0][0]+1是在&a[0][0]的地址上增加sizeof(a[0][0])的长度.
运行结果:&a = 0x7ffe5aae4420 &a[0] = 0x7ffe5aae4420 &a[0][0] = 0x7ffe5aae4420
&a+1 = 0x7ffe5aae4430 &a[0]+1 = 0x7ffe5aae4428 &a[0][0]+1= 0x7ffe5aae4424
注意:你的运行结果可能和我不同,因为地址是使用时分配的,每次分配的可能不一样。
6. 下列程序的功能是什么?有什么问题,你能找出问题并解决它吗?
int* get_array() {
int array[1121];
for (int i = 0; i < sizeof(array) / sizeof(int); i++) {
array[i] = i;
}
return array;
}
int main(int argc, char *argv[]) {
int *p = get_array();
}
警告:函数返回局部变量的地址。(我还没看,编译器就自己报出来了)
函数不能返回局部变量的地址,因为,出了函数后地址中的值就会被抛弃,可能你调用完函数马上用不会有问题,但你在使用这个值和函数之间加一条占用地址的语句,就会出错。
对于这种情况,可以用static局部静态变量来解决,或者从主函数传入地址。
int* get_array() {
static int array[1121];
for (int i = 0; i < sizeof(array) / sizeof(int); i++) {
array[i] = i;
}
return array;
}
int main(int argc, char *argv[]) {
int *p = get_array();
}
7. 下面代码的运行输出结果是什么,并说说你的理解。
int main(int argc, char *argv[]) {
char str[] = "XiyouLinuxGroup";
char *p = str;
char x[] = "XiyouLinuxGroup\t\106F\bamily";
printf("%zu %zu %zu %zu\n", sizeof(str), sizeof(p),sizeof(x), strlen(x));
return 0;
}
结果:16 8 25 24
sizeof和strlen的区别:
sizeof 是C 语言的一种单目运算符,strlen是个函数.
sizeof以字节的形式给出操作数的存储空间的大小,strlen 计算的是字符串的长度。
sizeof()的()中不能调用函数。
strlen是根据“\0”来判断是否结束的,所以strlen只能判断字符串.
指针64位下都是8字节,32位下4字节,strlen会计算上“\0”.
8. 如下程序,根据打印结果,你有什么思考?
int add(int *x, int y) {
return *x = (*x^y) + ((*x&y)<<1);
}
int a;
int main(int argc, char *argv[]) {
int b = 2020;
if(add(&b, 1) || add(&a, 1)) {
printf("XiyouLinuxGroup%d\n", b);
printf("Waiting for y%du!\n", a);
}
if(add(&b, 1) && a++) {
printf("XiyouLinuxGroup%d\n", b);
printf("Waiting for y%du!\n", a);
}
||是先判断左边的,如果为真就不再判断右边的,为假就判断右边的,只要有一个为真就为真。
&&是先判断左边的,如果为假就不判断右边的,否则就判断,只有两个都为真,才会是真。
9. 在下段程序中,我们可以通过第一步打印出 a 的地址,假如在你的机器上面打印结果 是 0x7ffd737c6db4;我们在第二步用 scanf 函数将这个地址值输入变量 c 中;第三 步,随机输入一个数字,请问最终输出了什么结果,你知道其中的原理吗?
void func() {
int a = 2020;
unsigned long c;
printf("%p\n", &a);
printf("我们想要修改的地址:");
scanf("%lx", &c);
printf("请随便输入一个数字:");
scanf("%d", (int *)c);
printf("a=%d\n",a);
}
a的值将会被修改,因为a这个变量指向的地址里的值被赋给了c,然后a和c指向了同一块地址,所以修改c的值,也会同步修改a的值,修改a的值也会影响c.
(第十题问c语言源程序到可执行文件的被我吃掉了)
11. 请解释一下这行代码做了什么?
puts((char*)(int const[]){
0X6F796958,0X6E694C75,0X72477875,
0X3270756F,0X313230,0X00000A
});
结果:XiyouLinuxGroup2021(小端)
大小端,16进制的ascii码,19年,20年都有。
12. 请随机输入一串字符串,你能解释一下输出结果吗?
int main(int argc, char *argv[]) {
char str[1121];
int key;
char t;
fgets(str, 1121, stdin);
for(int i = 0; i < strlen(str) - 1; i++) {
key = i;
for(int j = i + 1; j < strlen(str); j++) {
if(str[key] > str[j]) {
key = j;
}
}
t = str[key];
str[key] = str[i];
str[i] = t;
}
puts(str);
return 0;
}
这是字符串利用ascii码进行排序,按ascii码由小到大排序;
13. 用循环和递归求 Fibonacci 数列,你觉得这两种方式那种更好?说说你的看法。如果 让你求 Fibonacci 数列的第 100 项,你觉得还可以用常规的方法求解吗?请试着求 出前 100 项的值(tip 大数运算)。
我先忙猜循环,因为递归调用函数,会极大影响效率,(C++中inline函数改善了这种情况)
然后就是写代码分析了:
long recursive(int n){
if(n==1||n==2){
return 1;
}else{
return recursive(n-1)+recursive(n-2);
}
}
long loop(int n){
if(n == 1 || n == 2)
return 1;
long a = 1, b = 1, temp;
int i;
for(i = 3; i <= n; i++) {
temp = b;
b = a + b;
a = temp;
}
return b;
}
int main(){
int a=10;
scanf("%d",&a);
long int c=recursive(a);
printf("%ld\n",c);
long int b=loop(a);
printf("%ld\n",b);
return 0;
}
可以看出,递归的时间复杂度是O(2^n),循环的则是O(n),可以看出循环的效率远高出递归。
对于第100个斐波那契数,长度超出 long long,只能用大数计数:
( 前几天刚看的求第1000位数,改一下正好能求第100个的值):
int main(){
int fib[3][1005]={
{0,0},{1,1},{1,1}};//三个斐波那契数:f(n),f(n-1),f(n-2),各一维数组第一个位为位数计数。
int n=2;//n为第n位斐波那契数
while (n < 100) {
n += 1;
for(int i=1;i <= fib[(n - 1) % 3][0];i++){
fib[n%3][i]=fib[(n-1)%3][i]+fib[(n-2)%3][i];// 对应位置上的数字相加
}
fib[n%3][0]=fib[(n-1)%3][0];//将位数及时移动过来
for (int i = 1; i <= fib[n % 3][0]; ++i) { //处理进位
if (fib[n % 3][i] < 10) continue;
fib[n % 3][i + 1] += fib[n % 3][i] / 10;
fib[n % 3][i] %= 10;
fib[n % 3][0] += (i == fib[n % 3][0]);
}
}
for(int i=1;i<=fib[n % 3][0];i++){
printf("%d",fib[n%3][i]);
}
printf("\n");
}