复习小知识
- %p:以十六进制输出地址(指针)
- %x:以十六进制输出数值 如:0x1234
- %a(%A):浮点数、十六进制数字和p-(P-)记数法(C99)
- %c: 输出字符
- %d: 有符号十进制整数
- %f: 浮点数(包括float和doulbe)
- %e(%E): 浮点数指数输出[e-(E-)记数法]
- %g(%G): 浮点数不显无意义的零"0"
- %i: 有符号十进制整数(与%d相同)
- %u: 无符号十进制整数
- %o: 八进制整数 如:0123
- %s: 输出字符串
- %%: “%”
- %zu: 64下的unsigned long int
2020年
例1:
int main(int argc,char *argv[]){
char *str = "xi you linux group 20";
printf("%d\n",printf(str));
return 0;
}
printf():
正确返回输出的字符总数,错误返回负值。与此同时,输入输出流错误标志将被置值,可由指示器函数 ferror(FILE *stream) 来检查输入输出流的错误标志,如果 ferror() 返回一个非零值,表示出错。
所以输出了21。
例2.
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;
}
这道题目考察了对于位运算以及对数据类型的转换,其次是对2的幂次方的计算。
1.>>为为运算符号,是将一个数的二进制向右移动N位,因为我们一般的电脑存储为由低到高存储,所以高位舍弃,低位补0,又因为33554433 = 225 +1,所以当向右移动24位后为1再加上1变为0…010,也就是十进制的2,再加上‘?’ = 十进制的63便成为了 ASCII 为 65 的 ‘A’
2.强制类型转换,为了让我们新生学习到更多的知识选择用指针类型的强制转换方法再解引用.
例3.
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属于偏移了整体数组的大小,所以偏移的字节=2x2x4,&a[0]+1偏移量该二维数组下的一维数组整体的地址 = 2x4,&a[0][0]+1也就是偏移了一维数组的整体地址 = 1x4
例4:
int main(int argc, char **argv){
char str[] = "XiyouLinuxGroup";
char *p = str;
char x[] = "XiyouLinuxGroup\t\106F\bamily";
printf("%s",x);
printf("%zu %zu %zu\n",sizeof(str),sizeof(p),sizeof(x),strlen(x));
return 0;
}
注意转义符的用法,在cpp里也有提到,转义符后可以跟1个或多个16进制的数字,3个8进制的数字——>都将转义为ASCII对应的字符
例5:
int main(int argc, char **argv){
char str[2021];
int key;
char t;
fgets(str,2021,stdin);
for(int i = 0;i < strlen(str) - 1;i++){
key = i;
for(int j = i+1;j<strlen(str)-1;j++){
if(str[key] > str[j]){
key = j;
}
}
t = str[key];
str[key] = str[i];
str[i] = t;
}
puts(str);
return 0;
}
复习一下选择排序,将输入的字符串进行ASCII的由小到大排序
2019年
例1:
//问会打印多少个‘=’
int main(int argc,char *argv[]){
for(unsigned int i = 3;i >= 0;i--){
putchar('=');
}
return 0;
}
复习一下unsigned类型,这个题目有一个坑,就是 'i >= 0’这个判断条件,当’i–‘执行到i = 0时,会发现满足条件,当i再执行减减的条件时,由于unsigned int 是无符号整型,所以 i 等于0时再减减 i 就会变为4294967295,然后成为死循环.(若要判断i = 0这个条件的话可以改为while循环)
例2:
//交换两个数的解法
int a,b,c;
/*(1)*/ c = a;a = b;b = c;
/*(2)*/ a = a - b;b = b + a;a = b - a;
/*(3)*/ a = a ^ b;b = b ^ a;a = a ^ b;
第一种很常见,高中的交换两个瓶子中的水的问题,第二种,将其中a拆成数b+?的形式,将?赋值给a,再将b + a(?) 赋值给b,再把b - a(?)的值赋值给 a.第三种是异或运算,也就是同1同0得1,不同为0(记住公式即可^ _ ^ )
例3:
//下面会输出什么,解释其原因
int main(int argc,char *argv[]){
char ch = 255;
int d = ch + 2;
printf("%d %d",ch,d);
return 0;
}
还是考察了赋值,当给字符变量赋值整形数时为该整形数对应的ASCII值,255对应的为-1,所以在后面加2后赋值给int变量后会输出 1.
例4:
//下面的输出会是什么,解释输出
int main(int argc, char **argv){
printf("%d\n",printf("Xiyou Linux Group2%d",printf("")));
return 0;
}
考察了printf的特性,其输出后会返回`输出的字符的个数,所以打印出来了2019
例5:
//问下列会输什么样的代码段
int main(int argc, char **argv){
char x = -2,y = 3;
char t = (++x) | (y++);
printf("x = %d, y = %d t = %d\n",x,y,t);
t = (++x) || (y++);
printf("x = %d, y = %d t = %d\n",x,y,t);
return 0;
}
考察了或运算,运算符优先级,第一次的 t 输出的 -1 为运算的结果,第二次 t 输出的 1 为判断为真的返回值1
2018年
例1:
//思考下面输出的值,解释原因
int main(int argc, char **argv){
int nums[3][3] = {
1,2,3,4,5,6,7,8,9};
printf("%d\n",nums[1][-1]);
printf("%d\n",(-1)[nums][5]);
printf("%d\n",-1[nums][5]);
return 0;
}
由于数组的实质为nums[a] <=> *(nums + a),所以nums[a] <=> a[nums],然后就是地址偏移的问题
例2:
//思考下面输出的值,解释原因
int main(int argc, char **argv){
int a[3][2] = {
2,0,1,8};
char buf[256];
char *str = (char*)malloc(sizeof(char)*20);
strcpy(str,"\0101\\xb2");
printf("%zu\n",sizeof(a[0]));
printf("%zu %d\n",sizeof(a[1][1]),a[1][1]);
printf("%zu %zu %zu\n",sizeof(str),strlen(str),sizeof(buf));
return 0;
}
因为在对str malloc一个内存时,sizeof(str)会让其退化为指针的地址大小,所以无论malloc多大动态内存sizeof的结果都是该指针的内存大小一般为8不变,但是定义一个字符串数组时,若是栈内存则sizeof(buf)的结果为实际内存大小.
例3:
//输入2018,思考下面输出的值产生的原因
int f(unsigned int num){
for(unsigned int i = 0;num;i++){
num &= (num -1);
}
return 0;
}
这里复习了与运算,该题目将输出该2018二进制中有多少个1,很巧妙
例4:
//思考下面输出的值,解释原因
int main(int argc, char **argv){
char a[] = {
1,0,0,0};
printf("%s\n",a);
printf("%d\n",*(int*)a);
return 0;
}
当给字符串赋值整形,则会输出其对应的ASCII的字符,再对其进行数据强制类型转换输出 1
例5:
//思考下面输出的值,解释原因
#define YEAR 2018
#define LEVELONE(x) "XiyouLinux"#x"\n"
#define LEVELTOW(x) LEVELONE(x)
#define MULTIPLY(x,y) x*y
int main(int argc, char **argv){
int x = MULTIPLY(1 + 2,3);
printf("%d\n",x);
printf(LEVELONE(YEAR));
printf(LEVELTOW(YEAR));
return 0;
}
这里宏定义是 “#x” 中 “# ”是让 x 这个宏失效,所以在 “#define LEVELTOW(x) LEVELONE(x)” 这个语句中加了转换宏,使得这个宏处于中间层可以展开,所以 “printf(LEVELONE(YEAR));” 输出为XiyouLinuxYEAR,“printf(LEVELTOW(YEAR));” 输出为 “XiyouLinux2018”
例6:
//思考下面输出的值,解释原因
struct icd{
int a;
char b;
double c;
};
struct cdi{
char a;
double b;
int c;
};
int main(int argc, char **argv){
printf("%zu %zu\n",sizeof(struct icd),sizeof(struct cdi));
return 0;
}
对于字节对齐的问题,首先在结构体中是以字节最大的为准来分配内存,就比如说icd结构体中,因为有double类型,所以每次分配8个为准,int a 和 char b 都能在第一次分配的内存中放的下,所以之分配了两次,而对于cdi结构体,第一次分配8个字节不足以将 char a 和 double b都放下,所以第二次分配完后将double b 放入后再分配8个内存来存放int c
例7:
//思考下面输出的值,解释原因
typedef struct a{
char *name;
int num;
}A;
void func(A*a){
a = (A*)malloc(sizeof(A));
strcpy(a->name,"XiyouLinuxGroup");
a->num = 2021;
}
int main(int argc, char **argv){
A*a;
func(a);
printf("%s %d\n",a->name,a->num);
return 0;
}
因为在func函数中malloc的限定范围是在该函数中,所以程序直接挂掉(段错误)