练习分析小结(1)(复盘)

只要你愿意 开始总比放弃好。 Roman.

愿我们都有自己的目标并正在为其不懈努力。

-----------------------------------------------------------------------

一、 0124

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

#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测试,结果可能有差异。

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

#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--结果-1,i为整形,sizeof(i)求i类型大小是4,按照此分析来看,结果应该选择B,但是sizeof的返回值类型实际为无符号整形因此编译器会自动将左侧i自动转换为无符号整形的数据,-1对应的无符号整形是一个非常大的数字,超过4或者8,故实际应该输出 ">"

即:发生算术转换,具体向上转换方式见【操作符初阶】 (无符号则无原反补码概念)

  

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

#include <stdio.h>
int main()
{
	int a, b, c;
	a = 5;
	c = ++a;
	b = ++c, c++, ++a, a++;
	b += a++ + c;
	printf("a = %d b = %d c = %d\n:", a, b, c);
	return 0;
}

 *结果: a=9  b=23  c=8

 *分析:

++运算符:分为前置++和后置++,

前置++:(先加后用)先加1,后使用,即先使用变量中内容,然后给结果加1 

后置++:(先用后加)先使用变量中内容,整个表达式结束时,给变量加1

逗号表达式,取最后一个表达式的值

注意优先级:先赋值,后逗号表达式逗号表达式优先级最低

#include <stdio.h>
int main()
{
	int a, b, c;
	a = 5;
	c = ++a;// ++a:加给a+1,结果为6,用加完之后的结果给c赋值,因此:a = 6  c = 6
	b = ++c, c++, ++a, a++;
   // 逗号表达式的优先级最低,这里先算b=++c, b得到的是++c后的结果,b是7
   // b=++c 和后边的构成逗号表达式,依次从左向右计算的。
   // 表达式结束时,c++和,++a,a++会给a+2,给c加1,此时c:8,a:8,b:7
	b += a++ + c; // a先和c加,结果为16,在加上b的值7,比的结果为23,最后给a加1,a的值为9
	printf("a = %d b = %d c = %d\n:", a, b, c); // a:9, b:23, c:8
	return 0;
}

注意点:

1)赋值与逗号表达式同时出现且无括号进行限制,先赋值,再逗号表达式

2)前后置++、-- 的使用

3)前后置++、--均会改变原来的数

 4. 输入两个整数,求两个整数二进制格式有多少个位不同

(补:在线OJ有两种不同的形式:全部自己写 or 只写某一个函数的实现)

 1)方法一:按位与 以及 位移操作 (均针对二进制原码进行操作,n移位后n值不改变

 2)方法二:模2除2的计算&负数的强制转换

 3)方法三:n=n&(n-1):每次按位与都会减少二进制中一个1,直至n==0
    所以先异或,再找1的个数

#include<stdio.h>

//方法一:按位与 以及 位移操作
int Diff1(int a, int b)
{
	//先两个数异或:相同为0 相异为1
	int c = a ^ b;
	//计算1的个数即可
	int i = 0;
	int count = 0;
	for (i = 0; i < 32; i++)
	{
		if (1 == ((c >> i) & 1))  //注意 括号以及 优先级
		{
			count++;
		}
	}
	return count;
}

//方法二:模2除2的计算&负数的强制转换
int Diff2(int a, int b)
{
	unsigned int m = a;
	unsigned int n = b;
	int count = 0;
	while (m || n)
	{
		count += ((m % 2) ^ (n % 2));
		m /= 2;
		n /= 2;
	}
	return count;
}

//方法三:n=n&(n-1)
int Diff3(int a, int b)
{
	int count = 0;
	//每次按位与都会减少二进制中一个1,直至n==0
	//所以先异或,再找1的个数
	int tmp = a ^ b;
	while (tmp)
	{
		count++;
		tmp &= (tmp - 1);
	}
	return count;
}


int main()
{
	int a = 0;
	int b = 0;
	printf("请输入任意两个整数:\n");
	scanf("%d %d", &a, &b);
	int ret1 = Diff1(a, b);
	printf("方法一:异或&按位与 移位:%d\n", ret1);
	int ret2 = Diff2(a, b);
	printf("方法二:模2除2的计算&负数的强制转换:%d\n", ret1);
	int ret3 = Diff3(a, b);
	printf("方法三:n=n&(n-1):%d\n", ret1);
	return 0;
}

 4)补充:方法三:n = n & (n-1); 延伸题目:判断整数是不是2的幂次方

   (注:2的幂次方二进制表示只有一个1

#include<stdio.h>
int IsMi(int n)
{
	int count = 0;
	while (n)
	{
		count++;
		n &= (n - 1);
	}
	return count;
}
int main()
{
	int n = 0;
	printf("请输入任意一个整数:\n");
	scanf("%d", &n);
	int ret = IsMi(n);
	if (1 == ret)
	{
		printf("YES\n");
	}
	else
	{
		printf("NO\n");
	}
	return 0;
}

 5. 不允许创建临时变量,交换两个整数的内容

 1) 方法一:加减

 2)方法二:异或(注意:异或只能用在整数上

#include<stdio.h>

void Swap1(int* n, int* m)
{
	int tmp = *n + *m;
	*n = tmp - *n;
	*m = tmp - *n;
}

void Swap2(int* n, int* m)
{
	int tmp = (*n) ^ (*m);
	*n = tmp ^ (*n);
	*m = tmp ^ (*m);
}

int main()
{
	int n = 0;
	int m = 0;
	printf("请输入任意两个整数:\n");
	scanf("%d %d", &n, &m);
	printf("交换前:n=%d m=%d\n", n, m);
	int nn = n;
	int mm = m;
	Swap1(&n, &m);
	printf("方法一:加减:n=%d m=%d\n",n,m);
	Swap2(&nn, &mm);
	printf("方法二:异或:n=%d m=%d\n", nn, mm);
}

 6. 计算一个整数的二进制中1的个数(同样可以用题4中的三种方法) 

二、 0125

 1. 关于指针的概念,错误的是:( )

A.指针是变量,用来存放地址

B.指针变量中存的有效地址可以唯一指向内存中的一块区域

C.野指针也可以正常使用

D.局部指针变量不初始化就是野指针

 *结果:C

 *分析:

A:正确,指针变量中存储的是一个地址,指向同类型的一块内存空间

B:正确,地址是唯一的,一个指针变量中只能存储一个地址,因此可以唯一指向内存中的一块区域

C:野指针指向的空间是非法的,或者说该指针指向的空间已经不存在了,因此野指针不能使用

D:局部指针变量没有初始化时里面就是随机值,因此指向哪个位置不一定,故将其看成是野指针

补充:全局指针变量不初始化则默认初始化为NULL,地址也是0,但是没有访问权限

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

#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;
}

  *结果:0   0   3   4   5

 *分析:

arr数组在内存中的存储格式为:(小端存储)
0x00ECFBF4:  01 00 00 00
0x00ECFBF8:  02 00 00 00
0x00ECFBFC:  03 00 00 00
0x00ECFC00:  04 00 00 00
0x00ECFC04:  05 00 00 00
指针p的类型为short*类型的,因此p每次只能所有两个字节,for循环对数组中内容进行修改时,一次访问的是:
arr[0]的低两个字节,arr[0]的高两个字节,arr[1]的低两个字节,arr[1]的高两个字节,故改变之后,数组中内容如下:
0x00ECFBF4:  00 00 00 00
0x00ECFBF8:  00 00 00 00
0x00ECFBFC:  03 00 00 00
0x00ECFC00:  04 00 00 00
0x00ECFC04:  05 00 00 00
故最后打印:0   0   3   4   5

 *补充:注意强制类型转换,会对访问权限(步长)有影响

 3. 关于二级指针描述描述正确的是:( )

A.二级指针也是指针,只不过比一级指针更大

B.二级指针也是指针,是用来保存一级指针的地址

C.二级指针是用来存放数组的地址

D.二级指针的大小是4个字节

 *结果:B

 *分析:

A:错误,二级指针是指针,不能说起比一级指针大,只能说二级指针指向的空间中存储的也是一个地址

B:正确

C:错误,数组的地址一般用一级指针存储,或者用数组指针接收

D:二级指针是指针,但是否占4个字节不一定,要看具体的系统

注:二级指针是用来存放一级指针地址的

 4. 下面关于指针运算说法正确的是:( )

A.整形指针+1,向后偏移一个字节

B.指针-指针得到是指针和指针之间的字节个数

C.整形指针解引用操作访问4个字节

D.指针不能比较大小

 *结果:C

 *分析:

注意:此题是有问题的,说法不严谨,如果将整形指针理解成int*类型的指针,那么以下说法解析如下

A:错误,整形指针+1,向后跳过一个整形类型的大小,即4个字节

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

C:正确,整形指向的是一个整形的空间,解引用操作访问4个字节

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

补:不存在指针+指针

标准规定:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。

(详见【初阶指针Part(含指针比较大小)初阶指针(含指针比较大小)初阶指针(含指针比较大小)】)

 5. 逆序字符串 【字符逆序

*注意: 

逆序:不是逆序打印,需要进行元素交换

数组读取有空格字符:fgets(数组名,最多读取多少个字符,从哪里读取:stdin(标准输入))

gets(); 返回值类型是char*,读取失败or结束返回NULL(空指针)

 

三种读取含空格字符串的方式:gets(数组名);  scanf("%[^\n]",数组名);  fgets(数组名,max,stdin);(最后一种一般不用,可能会存在一些问题)

*代码:(注意计算字符个数:可以用指针-指针,也可以strlen(数组名)计算

//将一个字符串str的内容颠倒过来,并输出。
//数据范围: 1≤len(str)≤10000
//字符串str可以有空格

#include<stdio.h>
#include<string.h>
#define max 10000

int get_len(char* str)
{
	//指针-指针
	char* start = str;
	while (*str)
	{
		str++;
	}
	return (str - start);
}

char* Reverse(char* p, int len)
{
	char* pp = p;
	int left = 0;
	int right = len - 1;
	while (left < right)
	{
		char tmp = *(p + left);
		*(p + left) = *(p + right);
		*(p + right) = tmp;
		left++;
		right--;
	}
	return pp;
}

int main()
{
	char arr[max] = { 0 };
	// fgets()
	//scanf("%s", arr);  //不能读取空格!!
	//printf("输入带空格的字符串:\n");

    //方法一:
	//fgets(arr, 1000, stdin);

	//方法二:
	gets(arr);

	//方法三:
	//scanf("%[^\n]", arr); //输入带有空格的字符串

	//printf("字符逆序前:\n");
	//printf("%s\n", arr);
	int length = get_len(arr); //字符个数
	char* p = Reverse(arr, length);
	//printf("字符逆序后:\n");
	printf("%s\n", p);
	return 0;
}

 

 6. 求Sn=a+aa+aaa+aaaa+aaaaa的前5项之和,其中a是一个数字。
  例如:2 + 22 + 222 + 2222 + 22222

 *代码:

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

int main()
{
    int a = 0;
    int n = 0;
    printf("请输入基数a和项数n:\n");
    scanf("%d %d", &a, &n);//2 5
    int i = 0;
    int sum = 0;
    int k = 0;
    //2 + 22 + 222 + 2222 + 22222
    for (i = 0; i < n; i++)
    {
        //方法一: *
        k = k * 10 + a; //注意该种算法!!
        sum += k;

        //方法二:pow
       // k += a * (int)pow(10.0, (double)i);
       // sum += k;
    }
    
    printf("%d\n", sum);

    return 0;
}

 *结果:

 

 7. 求出0~100000之间的所有“水仙花数”并输出。
“水仙花数”是指一个n位数,其各位数字的n次方之和确好等于该数本身,
如:153=1 ^ 3+5 ^ 3+3 ^ 3,则153是一个“水仙花数”。

 *代码:

//循环里变量的变化将其限定在循环条件中,其他地方不要在比变化

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

int main()
{
    printf("0~100000之间的所有“水仙花数”:\n");
	int i = 0;
    for (i = 0; i <= 100000; i++)
    {
        //判断i是否是自幂数(水仙花数)
        //1. 计算i的位数 - n
        int n = 1; //注意初始值
        int tmp = i;
        while (tmp /= 10) //注意循环条件
        {
            n++;
        }
        //2. 计算每一位的n次方之和
        tmp = i;
        int sum = 0;
        while (tmp)//1234
        {
            sum += (int)pow(tmp % 10, n);
            tmp /= 10;
        }
        //比较
        if (sum == i)
        {
            printf("%d ", i);
        }
    }
    return 0;
}

 *结果:

 

 8.  打印菱形

 (注:只输入上半的行数,然后分上下两部分打印,还有 " " 与 "*"之分

       注意按行打印,每行打印顺序以及打印个数)

 *代码:

#include<stdio.h>

int main()
{
    int n = 0;
    printf("请输入你想打印菱形上半部分的行数:\n");
    scanf("%d", &n);
    printf("菱形如下:\n");
    //打印上半部分 n
    int i = 0;
    for (i = 0; i < n; i++)
    {
        //打印一行:分为打印空格与*,一定是先打印空格再打印*
        //打印空格
        int j = 0;
        for (j = 0; j < n - 1 - i; j++)
        {
            printf(" ");
        }
        //打印*
        for (j = 0; j < 2 * i + 1; j++) //只是代表打印的个数,与所在列无关
        {
            printf("*");
        }
        printf("\n");
    }

    //打印下半部分 n-1
    for (i = 0; i < n - 1; i++)
    {
        //打印一行
        //打印空格
        int j = 0;
        for (j = 0; j <= i; j++)
        {
            printf(" ");
        }
        //打印*
        for (j = 0; j < (n - 1 - i) * 2 - 1; j++)
        {
            printf("*");
        }
        printf("\n");
    }
    return 0;
}

 *结果:

 

 

 

------------------------一个人所有的愤怒都来源于对自己无能的痛苦。------------------------

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

'Dream是普通小孩耶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值