一、简单说下 int
、short int
、float
、double
、long int
、long long int
在 64 位机申请内存的大小。
64(左)与32位(右)的情况:实践出真知
#include <stdio.h>
int main(){
printf("%d\n",sizeof(char)); //1 1
printf("%d\n",sizeof(short int)); //2 2
printf("%d\n",sizeof(int)); //4 4
printf("%d\n",sizeof(long int)); //4 4
printf("%d\n",sizeof(long long int)); //8 8
printf("%d\n",sizeof(float)); //4 4
printf("%d\n",sizeof(double)); //8 8
printf("%d\n",sizeof(long double)); //16 12
return 0;
}
可见除了long double
外其余变量在64位与32位所占字节数时相同的
另有测试得出加unsigned
对所占字节数无改变 而任意指针类型在64位下占8个,32位下占4个
二、使用 c 语言完成五种交换值的方式。
-
最常用的中间变量交换法
-
常规版
int tep=a; a=b; b=tep;
-
封装函数的指针进阶版
void swap(int *a,int *b){ int tep=*a; *a=*b; *b=tep; }
-
宏替换无实际意义版
#define swap(a,b) do{int tep=a;a=b;b=tep;}while(0) //加do-while语句是为了防止与其他语句产生关联性
-
-
数学法
-
形式一
a=a+b; b=a-b; a=a-b;
-
形式二
a=a*b; b=a/b; a=a/b;
-
-
位运算
a=a^b; b=a^b; a=a^b;
-
利用符号优先级的巧妙方法
b在括号中被赋为a后并不影响原算式中b的值
-
形式一
a=b+(b=a)*0;
-
形式二
a=a+b-(b=a);
-
-
栈实现(
createstack
函数以及top
函数省去)压栈
push
弹栈pop
返回当前栈顶元素top
创建一个栈头createstack
typedef struct stack{ int data; struct stack *next; }NODE,*Stack; void push(int n,Stack s){ Stack node=(Stack)malloc(sizeof(NODE)); assert(node); node->element=n; node->next=s->next; s->next=node; } void pop(Stack s){ Stack node; if(s->next==NULL){ printf("there is a false\n"); return; } node=s->next; s->next=node->next; free(node); } void swap(int *a,int *b){ Stack s=createstack(); push(*a,s); push(*b,s); *a=top(s); pop(s); *b=top(s); }
先将a压入栈中,再压入b,将栈顶元素b的值赋给a,最后弹出b将栈顶元素a的值赋值给b
三、下面代码段将打印出多少个“=” ? 运用相关知识解释该输出。
#include <stdio.h>
int main(){
for(unsigned int i = 3;i>=0;i--){
putchar('=');
}
}
unsigned
的范围从0到一个正数,所以i的值永远不会为负数,死循环无限输出=
四、逻辑关系的爱恨情仇。简述这个逻辑的运算过程以及输出 answer 的值。
#include <stdio.h>
int count = 100;// 简述逻辑
//这里的count是一个全局变量,作用域和生存域都是整个代码区
int process(int num) {
while (1) {
//死循环,就算switch走完一遍没有目标也会继续重新执行,除非找到return离开函数
switch (num) {
case 0: //这是旅途的第一站,对同一个数进行两次相同的异或运算后该数不变,则此处实际是0|10=10
//逻辑或是有1则1,全0才0,逻辑异或是相同则0,不同则1
num = num ^ 99 ^ 88 ^ 99 ^ 88 | 10;
case 15: //上面没有break,还在第一站,10^5=15
//第二站进来15^5=10
num = num ^ 5;
break;
case 8: //第四站,num=1*2的八次方,即256,随后这个表达式即num=num-(num++),num还没来得及加就被减为0了,随后加为1
//左移实际上等价于*2的num次方
num = 1 << num;
num -= num++;
break;
case 10: //第三站,10-3=7,count=100+7=107,此后num变为8
num = num + - 3;
count = count + num++;
break;
case 2: //第六站,num变为1
--num;
case 5: //没有break,仍然是第六站,但有return,会跳出process函数,1<<1即2,answer的值也就明朗了
return (1 << num);
default: //没有1的情况,所以这里是第五站,各部位符号优先级相同,则从左往右算,1&1=1,条件为真,执行num+1,即num=2
//逻辑与是有0则0,全1才1,三元表达式a?b:c意为a如果非0执行b,否则实行c
num = num & 1 ? num + 1 : num;
}
}
}
int main() {
int start = 0;
int answer = process(start); // 输出结果
printf("answer == %d\n", answer);
return 0;
}
五、struct
与 union
的故事。
#include <stdio.h>
typedef struct str {
short e1 ; //2占八分之二
char e2 ; //1占八分之三
char e3 [ 6]; //6原来的用不完,重开一个占八分之六
double e5 ; //8原来的用不完,重开一个占满
int e6 ; //4占八分之四
}str ; //则该结构体所占内存为32
typedef union uni {
short e1 ; //2
char e2 ; //1
char e3 [ 6]; //6
struct str e4 ; //32
double e5 ; //8
int e6 ; //4
}uni ; //则该联合体所占内存为32
int main(){
int x = sizeof (str);
int y = sizeof (uni);
printf ("x = %d ,y = %d \ n " , x , y);
printf ("hello = %d \ n " , printf ("3G = %d \n " , --x == 23 && x-- == 22 && ++y == 9));
//逻辑运算只算了--x,x的值变为31
//其次后面的printf返回9并把其本身的内容输出
//最后前面的printf输出其本身的内容
printf ("x1 = %d ,y1 = %d \ n " , x , y);
return 0 ; }
此题考点一在于结构体的内存对齐规则☞结构体中的各部分所占内存块会与占内存最大的那个变量对齐
当然也有办法让这个规则失效,在预处理阶段使用#pragame pack()
可以使各部分强制按照()中的数对齐
注:括号中的数只能为1,2,4,8
考点二在于联合体的内存对齐规则☞联合体的内存为其各部分中所占内存最大的那部分
考点三在于逻辑运算的返回值
-
逻辑运算的返回值一般为1(真)或0(假),且存在短路现象
-
短路:
&&
前的结果为假则不进行其后的运算 同样||
前的结果为真则不进行其后的运算 -
x–先判后算,–x先算后判
考点四在于printf
的返回值,其返回值是其""
中的字符数
注:\n
和%d
都只占一个字符 也占一个字符
六、宏函数基础。
#include <stdio.h>
#define SQR(x) x*x
int main() {
int i = 1 , j = 2 ;
printf ("%d \ n " ,SQR ( i + j));
reutrn 0;
}
宏函数的替换很单纯 真的只是替换
所以SQR(i+j)
会被替换为i+j*i+j
遵循四则运算法则故答案为5
七、小雪今天也有学习指针。
//以下程序运行结果是什么?
#include <stdio.h>
int main() {
int nums [ 2][ 5]={{ 2 , 4 , 6 , 8 , 10},{12 ,14 ,16 ,18 ,20}};
//nums是一个由两个小数组组成的大数组
int *ptr1=(int*)(&nums + 1); //先对nums取地址,其实和nums一样,也是对大整体加1加到了数组外面
int *ptr2=(int*)(&nums [ 0]+ 1); //对nums[0]进行取地址,导致其+1的对象变为了一个小数组,加到了下一个小数组的第一个
//此处为什么进行强制类型转化将在下面说明
printf ("*nums[0][4]+1 = %d \ n " ,nums [ 0][ 4]+ 1); //第一个小数组的第五个数字,即10,再加1为11+
printf ("*(nums[0])+1 = %d \ n ",*(nums [ 0])+ 1); //此处先对nums[0]取值,实际是*(nums[0][0]),即2,再加1为3
printf ("*(nums[0]+1) = %d \ n ",*(nums [ 0]+ 1)); //nums[0]即nums[0][0],加1后为nums[0][1],取值后为4
printf ("*(nums[1]+1) = %d \ n ",*(nums [ 1]+ 1)); //同上取值后为14
printf ("*(nums+1) = %d \ n ",*(nums + 1)); //此处对nums整个大整体加1,会加到一个未定义的区域,取值后无法估值
printf ("*(ptr2 -1) = %d \ n ",*(ptr2 - 1)); //由于二维数组实际上内存是连续分布的,第二个小数组第一个减1后到了nums[0][4],即10
printf ("*(ptr -1) = %d \ n ",*(ptr1 - 1)); //由数组外面不存在的“nums[2][0]”-1后回归到了nums[1][4],即20
return 0 ;
}
关于ptr1
和ptr2
指向地址前先进行强制转化的问题:
nums
本质上是一个二级指针,是一个指向五个一级指针的二级指针,而ptr1
和ptr2
是两个一级指针,直接使一级指针指向二维数组会报错
进行强制类型转化时会发生数据的截断
八、上一个被欺负的指针找来了她的男朋友。
//以下程序运行结果是什么?
#include <stdio.h>
int main() {
int a [ 3][ 4 ] = {{ 1 , 2 , 3 , 4}, { 3 , 4 , 5 , 6}, { 5 , 6 , 7 , 8}};
int i ;
int(* p)[ 4 ] = a,*q = a [ 0];
//由于(*p)首先是一个指针,所以p是一个指向数组的指针;易得q指向的a[0]为1
for (i = 0 ; i < 3 ; i++) {
if (i == 0 ) (* p)[i + i / 2 ] = *q + 1 ; //i+i/2还是0,将p指向第一个数组的第一个数改为了1+1=2
else p++,++ q ; //i总共还能执行两次,p,q均向后移了两位,只是p是以一个数组的长度进行移动,而q是单个移动
}
for (i = 0 ; i < 3 ; i++) {
printf ("a[%d][%d] = %d \ n " , i , i , a [ i][ i]);
} //输出三个值,即a[0][0]=2,a[1][1]=4,a[2][2]=7
//此处需要注意a[0][0]在之前已经被p改变为2
printf ("%d, %d \ n " , *((int*) p), * q);
//p其实也可以理解为一个二级指针,所以要先进行强制类型转换再取值,p此前移动到了第三个数组,强转后再取值为5
//q移动后的值即3
}
九、那就简单排个序吧 。
解释这段代码的作用,以及代码逻辑和相关用法,以及这些函数的特性与作用。
#include <stdio.h>
int int_cmp(const void * p1, const void * p2) {
return (*( int *)p1 - *(int *) p2); } //将p强制转化后取值
//比较函数,经常改变形式以适用于qsort函数
//const void*是其适用性广泛的关键,可以任意强制转化来进行相应类型的比较
//如果p1比p2大则会返回1,相等返回0,否则返回-1
void _swap(void *p1, void * p2, int size) {
int i = 0;
for (i = 0; i< size; i++) {
char tmp = *((char *)p1 + i);
*(( char *)p1 + i) = *((char *) p2 + i);
*(( char *)p2 + i) = tmp;
}
}
//看上去写的很复杂,但实际上就是用中间变量交换两个变量的值
//值得注意的是如果p1和p2是两个数组,该交换函数仍然能交换它们所有值,适用性极强
void Qsort(void *base, int count, int size, int (*cmp)(void *, void *)) {
//此处调用比较函数借助了一个函数指针cmp去调用函数,通常称其为回调函数
//函数指针通常只用将函数原型里的函数名替换为*指针名即可
//用函数指针去调用函数,指针只管调用,不用关心具体的函数内容,将调用与函数的关联性削弱
//如果遇到了两个相似形式的函数,就可以直接用函数指针改变部分参数去同时调用两个函数
int i = 0;
int j = 0;
for (i = 0; i< count - 1; i++) {
for (j = 0; j < count-i-1; j++) {
if (cmp ((char *) base + j*size , (char *)base + (j + 1)*size) > 0)
//如果比较函数判断大于0,即前者比后者大就执行交换函数,该处比较的始终是两个相邻的元素,可见其本质是冒泡排序
//两个for循环不断遍历最终完成排序
{
_swap(( char *)base + j*size, (char *)base + (j + 1)*size, size);
}
}
}
}
//
int main() {
int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
int i = 0;
Qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), (int (*)(void *, void *)) int_cmp);
//qsort函数位于stdlib库中,原理是快速排序
//qsort(要排序的数组,需要排序的元素个数,一个元素的大小,比较函数)
for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++) {
printf( "%d ", arr[i]);
}
printf("\n");
return 0;
}
初看时快排,实际上为冒泡,同时该题处处都在考察对适用泛用性的理解
十、我爱 学数学
下面的算式中,不同的汉字表示不同的数字,相同的汉字表示相同的数字,问这个算式中每个 汉字各代表什么数字?(提示:枚举)
(使用 C 语言向学长学姐解释解题思路及代码,请提前准备好代码,如果不会也没关系可以向 学长学姐讲述一下自己的想法)
爱 = () 数 = ()学 = ()啊 = ()
int x,y,z,t;
for(x=0;x<10;x++){
for(y=0;y<10;y++){
for(z=0;z<10;z++){
for(t=0;t<10;t++){
if(x*102+y*10+z*10+t*100==y+x*10+z*100+t*1000&&x!=y)
printf("爱=(%d)\t数=(%d)\t学=(%d)\t啊=(%d)\n",t,z,x,y);
}
}
}
}
设元乘上各个位数相加,暴力求解即可,除去四个0的情况故加一个x!=y
的限制条件
十一、你了解几种排序算法?简单说一下。
- 冒泡排序(原始版)
void BubbleSort(int n,int*s){
int tep;
for(int j=0;j<n;j++){
for(int k=1;k<n;k++){
if(s[k-1]>s[k]){
tep=s[k];
s[k]=s[k-1];
s[k-1]=tep;
}
}
}
}
- 选择排序(原始版)
void SelectSort(int n, int* s){
for(int i=0;i<n-1;i++){
int min=i;
for(int cnt=i+1;cnt<n;cnt++){
if(s[cnt]<s[min]){
min=cnt;
}
} //每轮找到一个最小值与该轮第一个值交换
if(min!=i){
int temp=s[i];
s[i]=s[min];
s[min]=temp;
}
}
}
- 快速排序(原始版)
void quicksort(int *m,int n){
if(n<2)return;//递归的结束条件
int pleft=0,pright=n-1;
int judge=1,middle=m[0];//middle为基准值,比它小的放在左边,比它大的放在右边
while(pleft<pright){
if(judge==1){
if(m[pright]>=middle){
pright--;
continue;
}//直到找到一个比middle小的需要放到左边的为止
m[pleft]=m[pright];
pleft++;
judge=0;
continue;
}
if(judge==0){
if(m[pleft]<=middle){
pleft++;
continue;
}//同上 找到一个大的需要放到右边的为止
m[pright]=m[pleft];
pright--;
judge=1;
continue;
}
}
m[pleft]=middle;//将被拿出来的基准值放到空出来的位置上
quicksort(m,pleft);//先将左边一直递归排序完
quicksort(m+pleft+1,n-pleft-1);//排完左边后对右边一直递归排序
}
- 归并排序(递归原始版)
//在mosrt分为每个单份后的合并
void merge(int *arr,int *just,int left,int middle,int right){
int l_pos=left;//左边的第一个元素
int r_pos=middle+1;//右边的第一个元素
int pos=left;//临时存放点
while(l_pos<=middle&&r_pos<=right){
//每次总是往临时数组中放入左右两个区间中更小的元素
if(arr[l_pos]<arr[r_pos]){
just[pos++]=arr[l_pos++];
}
else{
just[pos++]=arr[r_pos++];
}
}
//下面两个循环不会同时执行,只会出现一边排得更快导致另一边没排完需要继续排的结果
while(l_pos<=middle){
just[pos++]=arr[l_pos++];
}
while(r_pos<=right){
just[pos++]=arr[r_pos++];
}
//将临时数组just中的值返回给arr
while(left<=right){
arr[left]=just[left];
left++;
}
}
//归并前将各个数字分开
void msort(int *arr,int *just,int left,int right){
//保证递归后每个区间只有一个元素
if(left<right){
int middle=(left+right)/2;
//将左边与中间元素分为一组
//中间往后第一个元素与最后一个元素分为一组
msort(arr,just,left,middle);
msort(arr,just,middle+1,right);
//将已经分为各个单一的数字归并
merge(arr,just,left,middle,right);
}
}
//归并前创建存储数组just
void merge_sort(int *arr,int n){
int *just=(int*)malloc(n*sizeof(int));
if(just){
msort(arr,just,0,n-1);
free(just);
}
else{
printf("lost malloc");
}
}