EOF的应用
EOF表示end of file,即文件结尾。!=EOF表示没有到达文件结尾,即还有数据资料可以读取,EOF实际ASCII值为-1,即-1==EOF为真,而ASCII的范围为0~127,所以常用while(!=EOF)来表示文件不结束就循环。
scanf在返回失败时,会返回一个EOF;一般语句正确执行都会返回一个1。
例子如下:
int main()
{
int i = 0;
while (scanf("%d", &i) != EOF) //可以实现循环输入
{
int ret = i & 1;
if (ret == 1)
printf("Odd\n");
else
printf("Even\n");
}
return 0;
}
另一种实现循环输入的方法是写成while(~scanf("%d",&i))
因为EOF=-1,则EOF的补码为11111111111111111111111111111111,所以EOF取反后为00000000000000000000000000000000,从而实现结束循环。
打印地址
%p为十六进制形式的地址 %d为整型数据(十进制)%x为整形数据(十六进制)
%p和%x的区别就在于%p不会省略数字,而%x可能会把高位的零省去。
测试代码如下:
#include <stdio.h>
int main()
{
int a = 9;
for (int i = 0; i < 3;a++, i++)
{
printf("用p直接打印(不取地址):%p\n", a);
printf("用p打印(取地址):%p\n", &a);
printf("用x直接打印(不取地址):%x\n", a);
printf("用x打印(取地址):%x\n", &a);
printf("用d直接打印(取地址):%d\n", a);
printf("用d打印(取地址):%d\n\n", &a);
}
return 0;
}
得到结果
可见要打印地址需要满足两个条件:①使用%p ②取地址
输入字符串和输入字符
① 首先明确需要用%s来输入字符串,且需要将字符串输入字符数组中,有一下 要注意的点:
*输入时候对scanf,&arr即可,就获得了数组的地址,写成&arr[100]这样是错误的,变成了取特定下标的地址了
*输出时候对printf,arr即可,与上同理
例子如下;
int main()
{
char a[100];
scanf("%s", &a);
printf("%s", a);
return 0;
}
②单个字符输入时候,缓冲区中会出现字符以及\n(字符串结尾),所以当选取单个字符使用时,需要将\n隔离开,否则缓冲区中留下的\n可能会干扰程序运行。
e.g.1
{
char ch = 0;
while (~scanf("%c"), &ch)
{
if (ch != 'U')
{
printf("Vowel\n");
break;
}
else
printf("Const\n");
getchar();//getchar相当于进行下一次循环之前把缓冲区域的\n拿掉
}
return 0;
}
e.g.2
{
char ch = 0;
while (~scanf("%c\n"), &ch)//此处的\n相当于scanf取值时直接把字符ch和\n当成一个整体取走
{
if (ch != 'U')
{
printf("Vowel\n");
break;
}
else
printf("Const\n");
}
return 0;
}//此法仅限于打字符时候拿走\n,其他不一定管用
e.g.3
{
char ch = 0;
while (~scanf(" %c"), &ch)//%c前加空格,会跳过空白字符(\n就是一种空白字符 会被跳过)
{
if (ch != 'U')
{
printf("Vowel\n");
break;
}
else
printf("Const\n");
}
return 0;
}
gets函数
gets是一个作用类似于scanf的输入函数。当输入随机长度的字符串时,需要先开辟足够空间的字符数组内存,但是输入的字符串不一定会占满内存,所以输入字符串就要用到gets函数。
要将一整行字符串输入到字符数组中时,gets函数非常常用。
注:VS 2019中 gets函数未定义,gets_s(首元素地址,空间长度)可发挥相同作用。
int main()
{
char a[100] = {0};
gets(a); //输入Hello!
printf("%s",a); //输出Hello!
return 0;
}
printf的输入
printf函数的输入是按照传入参数的类型决定的,若是%d就是四个字节,若是%lf就是八个字节,只有long long,double,float(float会被转化成double再传入)是八个字节。
int main()
{
unsigned char a = 200;
unsigned char b = 100;
unsigned char c = 0;
c = a + b;
printf(“%d %d”, a+b,c);
return 0;
}
输出的结果是300 44,因为printf输入类型为都为%d,所以是四个字节,a是200(<256,200的十六进制是C8,没有越界),b是100(<256,十六进制是64,没有越界),相加得到300。而c的类型是char,a+b得到300超过了两个字节能表示的最大数字256,则十六进制的最高位会被砍掉,剩下了44,所以c本身的值就是44,最后按照整型输出也只有44。
函数定义size_t strlen( const char *string );输入字符串地址,读取到\0时结束。
当字符数组初始化为0时,对数组输出的结果为0。如下代码输出结果为0。
int main()
{
char a[10] = { 0 };
printf("数组a[10]的字符串长度为%d", strlen(a));
return 0;
}
当字符数组不完全初始化时,输出结果为已初始化的数量,如下,得到结果为5.
int main()
{
char a[10] = { 1,2,3,4,5};
printf("数组a[10]的字符串长度为%d", strlen(a));
return 0;
}
当字符完全初始化,但后面的初始化值为0时如下,结果为5.
int main()
{
char a[10] = { 1,2,3,4,5,0,0,0,0,0};
printf("数组a[10]的字符串长度为%d", strlen(a));
return 0;
}
下代码输出结果为2.所以对于字符数组而言,存储0相当于存储了\0?
int main()
{
char a[10] = { 1,2,0,4,5,0,3,0,0,0};
printf("数组a[10]的字符串长度为%d", strlen(a));
return 0;
}
可见strlen对于字符数组是碰到0就停止计数。
双引号表达式
printf的声明如下:
int printf( const char *format [, argument]... );
printf()括号中通常为["" ,地址],逻辑大致为传递一个地址,让函数去读取地址中的数据。
看以下的一种情况:
char * str = "Hello world";
printf(str);
printf("Hello world");
这里就涉及到了双引号表达式的问题。若引号内是字符串,则双引号表达式的结果是字符串首字符的地址。所以可以在char* str时候后面直接写双引号表达式,同理也可以在printf中直接使用指针str作为参数。
强制类型转换
显示强制转换:TYPE a = (TYPE) b,如
int main()
{
int n = 257;
char i = (char)n;
printf("i=%d",i);//输出i=1
return 0;
}
void check()
{
int i = 1;
char* n = (char*)&i;if (*n)
//相当于if(*(char*)&i)
printf("小端字节序\n");
else
printf("大端字节序\n");
}
若不进行强制类型转换会报错,int不能直接赋给char,int*也不能直接赋给char*。
隐式强制转换:
int main
{
int n = 0;
double i = 3.88;
n = i;
printf("n=%d",n);//输出n=3
return 0;
}
系统判断为假的情况
Ⅰ:0
Ⅱ:EOF
Ⅲ:NULL
Ⅳ:'\0'
遍历数组时候用的多指针
有的时候要遍历一个数组,会需要用一个指针储存某个特定的位置,用另一个指针向前遍历。简单说就是对一个数组使用多个指针的情况,目前来说是两个指针比较常见。
比如说strstr函数,对字符串进行查找时候,若发现不是合适的起始点,就需要回到刚刚使用的起点位置,如果只有一个指针就无法记忆那个位置,对子字符串也是同理。设置一个光标指针和一个位置指针就可以比较清晰的解决问题,图解大致如下。