学习目标:
值得注意
学习内容:
1:对二维数组指针的认识:
①先设一个二维数组:int a[3][4]={{1,3,5,7},{9,11,13,15},{17,19,21,23}};
②:若将每行视作一个元素,此时的二维数组就成了一列,一维数组。自上往下编号:a[0]、a[1]、a[2]。那此时针对a[0]、a[1]、a[2]来说,它们自己就是一个一维数组,a[0]、a[1]、a[2]就是这三个行(一维数组)的数组名,我们知道,一位数组的数组名就是数组首元素地址!故:a[0]就是a[0][0]的地址,a[1]就是a[1][0]的地址,a[2]就是a[2][0]的地址。总结为公式一:a[i]=&a[i][0];
③:我们知道一维数组中,根据数组元素的数据类型去建立不同类型的指针变量是为了操作对应字节数。简言之,一个一维数组:b[M],b为数组首元素地址&b[0],而b+1就是第二个元素的地址!总结为公式,推广至二维数组,再结合公式一,得公式二:a[i]+j=&a[i][j].或者写成公式三:
&a[i][j]=&a[i][0]+j.
2:操作符详解
操作符分类
算术操作符 | +、—、*、%、/ |
---|
移位操作符 | >>、<< | 位操作符 | &、^、| | 赋值操作符 | =、+=、-=、/=、%=、>>=、<<=、&=、|=、^= | 单目操作符 | sizeof()、++、--、!、&、* | 关系操作符 | >、<、>=、<=、==、!= | 逻辑操作符 | &&、|| | 条件操作符 | exp1?exp2:exp3 | 逗号表达式 | exp1,exp2,exp3,... | 下标引用、函数调用、结构体成员 | []、()、. 、—> |
2.1:一些值得注意的点
①:%只可用于整数,而其他几个算术操作符可同时整数及浮点数,返回整数相除后的余数?
②:对于/:除数及被除数都是整数,则结果为整数,两者出现浮点数,则结果就是浮点数
③:对移位操作符,移位数必须是正整数。
? ? ? ??左移位:“左边丢弃,右边补零”
? ? ? ? 右移位:(两种):
逻辑移位 | “右边丢弃,左边补零” | 算术移位 | “右边丢弃,左边补原补码符号位” |
④:位操作符都是对二进制补码进行操作,说出补码,就应该知道它们是针对整数而言的。
⑤:一个曾经考过的笔试题:
设计程序,看给一个整数,其在内存存储是,有多少个1?
#include <stdio.h>
int bit_one_num(int x)
{
int count = 0;
int i = 0;
for (i = 0; i < 32; i++)
{
if ((x & 1) == 1)//可优化成:if(x&1)
{
count++;
}
x >>= 1;
}
return count;
}
int main()
{
int n = 0;
scanf("%d", &n);
int ret=bit_one_num(n);
printf("%d\n", ret);
return 0;
}
⑥:一个老生常谈的笔试题:
不使用第三变量实现两个数的交换
法一:(常规的写法,有引入第三变量)
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
int c = a;
a = b;
b = c;
printf("a=%d,b=%d\n", a, b);
return 0;
}
法二:(数字大了可能溢出int所能存储的INT_MAX/INT_MIN)
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
a = a + b;
b = a - b;
a = a - b;
printf("a=%d,b=%d\n", a, b);
return 0;
}
法三:(只对能存进去的补码进行了操作,所以不会有溢出问题,且该过程未引入第三变量,缺点:可读性不好,只适用于整型!因为位操作符只适用于整数)
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
a = a ^ b;
b = a ^ b;
a = a ^ b;
printf("a=%d,b=%d\n", a, b);
return 0;
}
⑦:注意区分:&arr[0]+1和&arr+1的区别,前者向高地址方向跳一个数组类型字节数,后者往高地址方向跳整个数组字节数。
⑧:sizeof是操作符不是函数,sizeof求类型字节数时,不可省略括号,求变量字节数时可省略括号。能省略括号,也说明它是个操作符而不是函数。
⑨:注意&&和||使用时:前者从左往右,遇到假表达式,后面就直接不进行计算了!后者是从左往右遇到真表达式,后面也直接不计算了!!!这非常容易导致一些参数的变化不符合逻辑。
⑩:结构体变量作为实参传给函数时,所有东西都传过去了,因为这里没有用指针的结构体成员符号:->,不像数组那样,只传一个首地址。但不是说结构体不给传地址。如:
?十一:上述代码中:a.name是数组名name,所以它是一个首地址,说这个是因为有个操作易错:如要修改“C语言程序设计”为“数据结构”,错误写法:a.name=“数据结构”;因为不可以把一个东西放在或者说赋给地址,正确操作:使用srcpy:strcpy(a.name,“数据结构”);顺便介绍一下strcpy:
格式:char* strcpy(char* destination,const char* source);自source起拷,拷至该字符串末尾的‘\0’,注意'\0'也被拷进去了啊!,然后从destination开始放,所以要能放得下!!返回的是destination,const是防止source指向的对象被修改,提高代码健壮性!
十二:对于隐式类型转化,总结了一个很好用的口诀:屏幕上的都是原码,内存里的都是补码,想自己用二进制算东西,用补码。数据类型决定向内存申请多少空间,常用的32编译器,手算结果时都是直接根据屏幕写出原码,推至补码,怎么推就不用说了吧?然后去算,存时看申请的空间是多少,考题一般都是截断哦(保留低位),对于char和short,因为要发生整型提升,此时的signed和unsigned至针对1(2)字节的最高二进制位而言的哦!整形提升有路(正数还是负数?)了哦!对于%d和%u总是针对32位(补码)最高位而言的哦,是否要进行变原码的计算路子也有了哦!上述懂得都懂,能解决所有类似问题!
3:来道题压压惊
多组输入,针对每个输入的正整数,都可输入一个该数量下的空心正方形,正方形的每条边都是‘* ’,中间为空心的。
#include <stdio.h>
int main()
{
int n = 0;
while (scanf("%d", &n) != EOF)
{
int i = 0;
for (i = 0; i < n; i++)
{
int j = 0;
for (j = 0; j < n; j++)
{
if ((i == 0) || (i == n - 1))
{
printf("* ");
}
else
{
if ((j == 0) || (j == n - 1))
{
printf("* ");
}
else
{
printf(" ");
}
}
}
printf("\n");
}
}
return 0;
}
展示效果:
?
?时间比较紧,先分享这么多干货。
学习时间:
好几天了哦!
学习产出:
CSDN技术博客
GITEE
笔记
|