C语言练习——总结2

(自用)

 

1.sizeof和strlen的计算

答案详见博客

1.一维数组

//一维数组
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a+0));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof(a[1]));
printf("%d\n",sizeof(&a));
printf("%d\n",sizeof(*&a));
printf("%d\n",sizeof(&a+1));
printf("%d\n",sizeof(&a[0]));
printf("%d\n",sizeof(&a[0]+1));

2.字符数组

int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", sizeof(arr));
	printf("%d\n", sizeof(arr + 0));
	printf("%d\n", sizeof(*arr));
	printf("%d\n", sizeof(arr[1]));
	printf("%d\n", sizeof(&arr));
	printf("%d\n", sizeof(&arr + 1));
	printf("%d\n", sizeof(&arr[0] + 1));
	printf("%d\n", strlen(arr));
	printf("%d\n", strlen(arr + 0));
	printf("%d\n", strlen(*arr));
	printf("%d\n", strlen(arr[1]));
	printf("%d\n", strlen(&arr));
	printf("%d\n", strlen(&arr + 1));
	printf("%d\n", strlen(&arr[0] + 1));
	
	return 0;
}
int main()
{
	char arr[] = "abcdef";
	printf("%d\n", sizeof(arr));
	printf("%d\n", sizeof(arr + 0));
	printf("%d\n", sizeof(*arr));
	printf("%d\n", sizeof(arr[1]));
	printf("%d\n", sizeof(&arr));
	printf("%d\n", sizeof(&arr + 1));
	printf("%d\n", sizeof(&arr[0] + 1));
	printf("%d\n", strlen(arr));
	printf("%d\n", strlen(arr + 0));
	printf("%d\n", strlen(*arr));
	printf("%d\n", strlen(arr[1]));
	printf("%d\n", strlen(&arr));
	printf("%d\n", strlen(&arr + 1));
	printf("%d\n", strlen(&arr[0] + 1));
}
int main()
{
	char* p = "abcdef";
	printf("%d\n", sizeof(p));
	printf("%d\n", sizeof(p + 1));
	printf("%d\n", sizeof(*p));
	printf("%d\n", sizeof(p[0]));
	printf("%d\n", sizeof(&p));
	printf("%d\n", sizeof(&p + 1));
	printf("%d\n", sizeof(&p[0] + 1));
}
int main()
{
char *p = "abcdef";
printf("%d\n", strlen(p));
printf("%d\n", strlen(p+1));
printf("%d\n", strlen(*p));
printf("%d\n", strlen(p[0]));
printf("%d\n", strlen(&p));
printf("%d\n", strlen(&p+1));
printf("%d\n", strlen(&p[0]+1));
return 0;
}

3.二维数组 

int main()
{
	int a[3][4] = { 0 };
	printf("%d\n", sizeof(a));//
	printf("%d\n", sizeof(a[0][0]));
	printf("%d\n", sizeof(a[0]));
	printf("%d\n", sizeof(a[0] + 1));
	printf("%d\n", sizeof(*(a[0] + 1)));
	printf("%d\n", sizeof(a + 1));
	printf("%d\n", sizeof(*(a + 1)));
	printf("%d\n", sizeof(&a[0] + 1));
	printf("%d\n", sizeof(*(&a[0] + 1)));
	printf("%d\n", sizeof(*a));
	printf("%d\n", sizeof(a[3]));
}
    int arr[3][4] = { 0 };
	printf("%d\n", sizeof(*arr+1));//?

2.指针笔试题

1.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<string.h>
int main()
{
	int a[5] = { 1, 2, 3, 4, 5 };
	int* ptr = (int*)(&a + 1);
	printf("%d,%d", *(a + 1), *(ptr - 1));
	return 0;
} 

 结果:

2.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<string.h>
struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
    p = (struct Test*)0x100000;
	printf("%p\n", p + 0x1);
	printf("%p\n", (unsigned long)p + 0x1);
	printf("%p\n", (unsigned int*)p + 0x1);
	return 0;
}

结果: 

 整形指针加1,跳过1个整形,跳过4个字节;字符指针加1,跳过1个字符,跳过1个字节。而这里是自己定义的结构体,结构体大小为20,那么这里+1则跳过20个字节。那么p的值为0x100000时

1.p+0x1,对于结构体类型的p进行加1,需要跳过20个字节,对于16进制则为14,因此结果为0x100014

2. (unsigned long)p + 0x1 将结构体指针类型强制类型转换为unsigned long类型,16进制加减正常+1,因此结果为00100001。

3.(unsigned int*)p+0x1:将结构体指针类型强制类型转换为unsigned int*类型,是一个指针,需要增加一个指针的大小,即+4,结果为00100004。

3.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
 
int main()
{
	int a[4] = { 1, 2, 3, 4 };
	int* ptr1 = (int*)(&a + 1);
	int* ptr2 = (int*)((int)a + 1);
	printf("%x,%x", ptr1[-1], *ptr2);
	return 0;
}

结果: 

分析:

(int*)(&a+1):首先&a取出整个数组的地址进行加+1,也就是跨越了一个数组,将其转化为int*类型在赋给指针变量ptr。

(int)a+1:a此时为首元素地址(假如a的其实地址为0x0012ff40),a强制类型转换为int类型,+1则正常数字运算,对地址的值进行加1,跳过1个字节,运算后(0x0012ff41)。如果是int*类型的话,+1跳过4个字节,(假如a的其实地址为0x0012ff40,则+1后变成0x0012ff44)

4.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
 
int main()
{
	int a[3][2] = { (0, 1), (2, 3), (4, 5) };
	//                 1       3       5
	//逗号表达式的结果是最后一个表达式的结果
	int* p;
	p = a[0];
	printf("%d", p[0]);
	return 0;
}

 这道题考察了逗号表达式(0,1),(2,3),(4,5)为逗号表达式,取逗号最右边的结果,即1,3,5,此时二维数组变成了a[3][2]={1,3,5};也就是第一行为a[0]={1,3},a[1]={5,0},a[2]={0,0};

a[0]是二维数组第一行的数组名,对a[0]这个数组名没有&,没有单独sizeof,所以a[0]这个数组名表示数组首元素的地址,即a[0][0]的地址,a[0]---->&a[0][0]

因此p=a[0][0],p指向为首元素地址,p[0]=1

5.

int main()
{
int a[5][5];
int(*p)[4];
p = a;
printf( "%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
return 0;
}

 结果:

 &p[4][2] - &a[4][2]中间相差4个元素,而&p[4][2]为低地址处所以为-4。-4的补码在内存中存储,且被看作为地址:

10000000000000000000000000000100   //原码

111111111111111111111111111111111011   //反码

111111111111111111111111111111111100   //补码

%p转换成16进制输出(每4位二进制位为1位十六进制位),则转换为0xFF FF FF FC

6.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
 
int main()
{
	int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int* ptr1 = (int*)(&aa + 1);
	int* ptr2 = (int*)(*(aa + 1));
	printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));
	return 0;
}

分析:

&aa+1:&aa取出的是整个数组的地址,加1跳过整个数组的大小,指向整个数组的末尾。

*(aa+1):aa是数组首元素的地址,加1后aa+1为数组第2行元素的地址,即aa[1],

然后都-1因为是int*所以指针都往前4个字节,得到结果

7.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
 
int main()
{
	char* a[] = { "work","at","alibaba" };
	char** pa = a;
	pa++;
	printf("%s\n", *pa);
	return 0;
}

 char*a[]数组存放三个元素,分别是字符串a[0],a[1],a[2];a为首元素地址存放在char* *p指针中,pa=a[0],然后pa++,则pa此时指向的是a[1],有首元素地址打印在\0处结束。

8.

int main()
{
char *c[] = {"ENTER","NEW","POINT","FIRST"};
char**cp[] = {c+3,c+2,c+1,c};
char***cpp = cp;
printf("%s\n", **++cpp);
printf("%s\n", *--*++cpp+3);
printf("%s\n", *cpp[-2]+3);
printf("%s\n", cpp[-1][-1]+1);
return 0;
}

**++cpp

操作顺序: 先++,后解引用

指针cpp存储了cp的地址,因此初始先指向cp[0],之后指针cpp到cp[1](也就是红色箭头指向),cp[1]存储的是c+2的地址,因此有了c[2]内容的首元素地址,打印c[2]至'\0'

*--*++cpp+3

操作顺序:  ++,解引用,再--,解引用,最后解引用位置+3

首先++--的优先级最高根据结合远近,先对cpp进行++,但是注意由于上一题++*cpp,是对cp自加并且将自加的值赋给cpp,因此cpp的位置从上一题的位置开始。++cpp后解引用为*cp[2],cp[2]的内容为c+1的指针(也就是c+1的地址),而c+1的地址-1再解引用此时为c[0],而之后+3则指针指向第三个元素的后面,最后从该位置打印至结束。

*cpp[-2]+3

操作顺序:cpp[2]-2到cpp[0],对c[0]指向c[3]对c[3]解引用,再向后移动3个单位也就是第三关字母的后面开始进行打印

 从上个题目的位置,再-2到cpp[-2]==*(cpp-2)==cp[0],cp[0]存储的是c+3的地址,之后对c+3的地址解引用,先指向首元素地址,再+3,在第三个元素的后面,最后从该位置打印至结束

cpp[-1][-1]+1

cpp[-1][-1]==*(cpp-1)[-1]==cp[1][-1]==*((c+2)-1)==*(c+1)==c[1],对cpp-1地址解引用得到c+3,后解引用在-1,解引用得到NEW,指针指向首元素地址,+1改变指针指向,最后打印得到结果 

3.写一个函数返回参数二进制中的1的个数

比如:15 0000 1111 4个1  (一般讨论的是补码,原码在计算机没办法存储).二进制中1的个数

int count_one_bit(int num)
{
	int count = 0;
	while (num)
	{
		if (num % 2 == 1)
		{
			count++;
		}
		num /= 2;
	}
	return count;
}

int main()
{
	int num = 0;
	scanf("%d", &num);
	int ret = count_one_bit(num);
	printf("%d\n", ret);
	return 0;
}

当输入-1时,输出结果为0,这个函数有缺陷,只能计算大于0的数字。

对于-1来说存在二进制序列中为:

10000000 00000000 00000000 00000001 //原码-1

11111111 11111111 11111111 11111110 //反码-1

11111111 11111111 11111111 11111111 //补码-1

解法1.如果稍作修改变成unsigned int,则-1在内存中每一位都变成有效位。

int count_one_bit(unsigned int num)
{
	int count = 0;
	while (num)
	{
		if (num % 2 == 1)
		{
			count++;
		}
		num /= 2;
	}
	return count;
}

int main()
{
	int num = 0;
	scanf("%d", &num);
	int ret = count_one_bit(num);
	printf("%d\n", ret);
	return 0;
}

 解法2按位与1检测二进制1的个数(1&1=1,1&0=0)

int count_one_bit(unsigned int num)//int num也可以都是按位与,上一个是因为num除法运算有问题
{
	int count = 0;
	for (int i = 0; i <  32; i++)
	{
		if ((num >> i) & 1 == 1)
		{
			count++;
		}
	}
	return count;
}

解法3n=n&(n-1)

只要运算一次,n的二进制中最右边的一个1就会消失

 代码:

int count_one_bit(unsigned int num)
{
	int count = 0;
	while (num)
	{
		num = (num - 1) & num;
		count++;
	}
	return count;
}

4.获取一个整数二进制序列中所有偶数位和奇数位,分别打印出二进制序列

int main()
{
	int num = 0;
	int i = 0;
	scanf("%d", &num);
	for (i = 31; i >= 1; i-=2)
	{
		printf("%d", (num >> i) & 1);
	}
	printf("\n");
	for (i = 30; i >= 0; i -= 2)
	{
		printf("%d", (num >> i) & 1);
	}
	printf("\n");
}

结果: 

5.求两个数二进制中不同位的个数

编程实现:两个int(32)位整数m和n的二进制表达中,有多少个位bit不同?

int main()
{
	int m = 0;
	int n = 0;
	int count = 0;
	scanf("%d %d", &m, &n);
	int ret = m ^ n;
	while (ret)
	{
		ret = ret & (ret - 1);
		count++;
	}
	printf("%d", count);
}

结果:

 注:异或^

010异或001得到011

6.如何判断一个数是不是2的幂次方

注:2的幂次方的数,二进制中只有1个1

2-10 、4-100、8-1000、16-10000

解:只需判断表达式n&(n-1)是否等于0

7.下面代码的结果是:( )

#include <stdio.h>
int main()
{
    int i = 1;
    int ret = (++i)+(++i)+(++i);
    printf("ret = %d\n", ret);
	return 0;
}

答案:程序错误

表达式(++i)+(++i)+(++i),只有操作符的优先级和结合性,没法确定唯一计算路径。所以这个表达式可能因为计算顺序的差异导致结果是不一致的,所以表达式是错误的表达式。可以在VS和Linux gcc测试,结果可能有差异。

8.下面代码的结果是:

#include <stdio.h>
int i;
int main()
{
    i--;
    if (i > sizeof(i))
    {
        printf(">\n");
    }
    else
    {
        printf("<\n");
    }
    return 0; 
}

C语言中,0为假,非0即为真。

全局变量,没有给初始值时,编译其会默认将其初始化为0。(即i的值为0)

i的初始值为0,i--结果-1,i为整形,sizeof(i)求i类型大小是4,按照此分析来看,结果应该选择B,但是sizeof的返回值类型实际为无符号整形(unsigned int),因此编译器会自动将左侧i自动转换为无符号整形的数据,-1对应的无符号整形是一个非常大的数字,超过4或者8,故实际应该选择A

 

9.判断

在一个函数内复合语句中定义的变量在本函数范围内有效(复合语句指函数中的成对括号构成的代码)(?)

错误,复合语句中定义的变量只能在复合语句中使用

10.BC117 小乐乐走台阶

小乐乐上课需要走n阶台阶,因为他腿比较长,所以每次可以选择走一阶或者走两阶,那么他一共有多少种走法?

#include <stdio.h>
int Fib(int n)
{
    if (n <= 2)
    {
        return n;
    }
    else
    {
        return Fib(n - 1) + Fib(n - 2);
    }
}

int main()
{
    int n = 0;
    scanf("%d", &n);
    int ret = Fib(n);
    printf("%d\n", ret);
    return 

11.BC38 变种水仙花

#include <stdio.h>
int main(){
    for(int i=10000;i<100000;i++){  
        int a=0;                      
        int b=0;
        int c=10;
        int sum=0;
        int x=i;
        while(x/c!=0){               
           a=x%c;                   
           b=x/c;                   
           sum+=a*b;                
           c*=10;                   
      if(x==sum){       
        printf("%d ",x);
      }
    }
  return 0;
}

 

12.下面代码的结果是:( ) 

#include <stdio.h>
int main()
{
  int arr[] = {1,2,3,4,5};
  short *p = (short*)arr;
  int i = 0;
  for(i=0; i<4; i++)
  {
    *(p+i) = 0;
  }
   
  for(i=0; i<5; i++)
  {
    printf("%d ", arr[i]);
  }
  return 0;
}

答案: 

 13.错误提醒

 1.debug和release的环境下存放变量的不同

int main()
{
	int i = 0;
	int arr[10] = { 0 };
	for (i = 0; i <= 12; i++)
	{
		printf("hehe\n");
		arr[i] = 0;
	}
	return 0;
}
无线打印hehe

在debug版本下,随着数组下标的变化,地址是由低到高变化的。而开辟空间是先使用低地址再使用高地址,如果数组下标越界可能会导致程序死循环。release版本在空间存储变量由低到高,因此循环不会出现问题。

2. int* p=(int*)0x0012ff40和int a =0x0012ff40中的0x0012ff40区别

其中,0x0012ff40都是数值,而前者变成int*指针类型的数值,相当于地址存放在p中,而后者只是一个普通数值存放在a中。

 3.指针-指针得到是指针和指针之间的字节个数(?)

错误,两个指针相减,指针必须指向一段连续空间,减完之后的结构代表两个指针之间相差元素的个数

4.指针不能比较大小(?)

 指针中存储的是地址,地址可以看成一个数据,因此是可以比较大小的

14.打印水仙花数

求出0~100000之间的所有“水仙花数”并输出。

“水仙花数”是指一个n位数,其各位数字的n次方之和确好等于该数本身,如:153=1^3+5^3+3^3,则153是一个“水仙花数”

#include <stdio.h>
#include <math.h>

int main()
{
	int i = 0;
	for(i=0; i<=999999; i++)
	{
		int count = 1;
		int tmp = i;
		int sum = 0;
		//判断i是否为水仙花数
		//1. 求判断数字的位数
		while(tmp/10)
		{
			count++;
			tmp = tmp/10;
		}
     
		//2. 计算每一位的次方和
		tmp = i;
		while(tmp)
		{
			sum += pow(tmp%10, count);
			tmp = tmp/10;
		}
     
		//3. 判断
		if(sum == i)
			printf("%d ", i);
	}
	return 0;
}

结果: 

 15.关于Debug和Release的区别说法错误的是:( )

A.Debug被称为调试版本,程序调试找bug的版本

B.Release被称为发布版本,测试人员测试的就是Release版本

C.Debug版本包含调试信息,不做优化。

D.Release版本也可以调试,只是往往会优化,程序大小和运行速度上效果最优

A:正确,Debug为调试版本,一般在开发完成后发布工程前,调试代码都是在Debug模式下进行的B:正确,Release版本最终是要发送给用户的,发给用户的版本必须要没有问题,测试人员就是最后一个把关的C:正确,Debug版本是调试版本,编译器编译时会增加一些调试信息,编译器基本不会对其进行优化D:错误,Release版本是不能调试的,一般都是在Debug版本下调试的,Release版本一般编译器会进行大量的优化,删除无用的代码,指令的次序调整等,让其速度更快

 

16.将一个字符串str的内容颠倒过来,并输出。

void Reverse(char* str)
{
    char* left = str;
    char* right = str + strlen(str)-1;
    while(left < right)
    {
        char temp = *left;
        *left = *right;
        *right = temp;
        ++left;
        --right;
    }
}


int main()
{
    char str[] = "hello bit";
    Reverse(str);
    return 0;
}


// 注意:如果是在线OJ时,必须要考虑循环输入,因为每个算法可能有多组测试用例进行验证,参考以下main函数写法,
int main()
{
    char str[101] = {0};
    while(gets(str))//fgets(arr,10000,stdin)//stdin - 标准输入-键盘
    {
        Reverse(str);
        printf("%s\n", str);
        memset(str, 0, sizeof(str)/sizeof(str[0]));
    }
    return 0;
}

17.喝汽水问题

喝汽水,1瓶汽水1元,2个空瓶可以换一瓶汽水,给20元,可以喝多少汽水(编程实现)。

*
思路:
1. 20元首先可以喝20瓶,此时手中有20个空瓶子
2. 两个空瓶子可以喝一瓶,喝完之后,空瓶子剩余:empty/2(两个空瓶子换的喝完后产生的瓶子) + empty%2(不够换的瓶子)
3. 如果瓶子个数超过1个,可以继续换,即重复2
*/
int main()
{
	int money = 0;
	int total = 0;
	int empty = 0;


	scanf("%d", &money);
	
	//方法1
	total = money;
	empty = money;
	while(empty>1)
	{
		total += empty/2;
		empty = empty/2+empty%2;
	}

    printf("total = %d\n", total);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值