C语言基础知识总结
本文的目的是记录平时工作学习过程中学习到的C语言知识,由于单独一篇文章记录的话可能篇幅过少,因此置顶此文用作此用处,本文从2017-12-16开始记录,后续新增内容不单独列出时间,在MarkEditor中有相应的版本记录。
文件描述符
EOF(end of file)文件结束,参考如下代码:
#include <stdio.h>
int main()
{
int c;
while((c = getchar())!=EOF)
{
printf("%c",c);
}
printf("\n");
printf("Last time %d\n",c);
return 0;
}
当输入的字符不是EOF时会打印输入的字符,然后输入EOF时退出while循环,我们通过printf打印出该值,之所以不把c声明为char,是因为它必须足够大,除了能存储任何可能的字符外还要能存储文件结束符EOF。
下面的程序是统计输入的行数 单词数和字符数,程序虽小,短小精悍:
#include <stdio.h>
#define IN 1
#define OUT 0
int main()
{
int c,nl,nw,nc,state;
state = OUT;
nl = nw = nc = 0;
while((c = getchar()) != EOF)
{
++nc;
if(c=='\n')
++nl;
if(c==' '|| c=='\t'|| c=='\n')
state = OUT;
else if(state == OUT)
{
state = IN;
++nw;
}
}
printf("%d %d %d\n",nl,nw,nc);
return 0;
}
该程序对单词的定义比较宽,它是任何不包含空格、制表符或换行符的字符序列。
函数参数为空
为了与老版本的C语言程序兼容,ANSI C语言把空的参数表看成老版C语言的声明方式,并且对参数表不再进行任何检查。在ANSI C中如果要声明空参数表,则必须使用关键字void进行显示声明。
“定义”:表示创建变量或分配存储单元
“声明”:说明变量的性质,但并不分配存储单元
类型 运算符 表达式
‘x’ 与“x”的区别:
‘x’是一个整数,其值是字母x在机器字符集中对应的数值。
“x”是一个包含一个字符以及一个结束符’\0’的字符数组。
define语句与枚举的区别:
define编译器不检查这种类型变量中存储的值是否为该枚举的有效值。枚举变量提供这种检查,因此枚举比define更具优势。
默认情况下外部变量的值与静态变量的值将被初始化为0.未经显示初始化的自动变量的值为未定义。
按位运算符
我们必须将按位运算符& | 同逻辑运算符&& ||区分开来,后者用于从左至右求表达式的真值。例如,如果x的值为1,y的值为2,那么,x&y的结果为0,而x&&y的值为1.
x=x&~077
设置x的最后6位为0,表达式x=x&~077与机器字长无关,它比形式为x&0177700的表达式要好,因为后者假定x是16位的数值,这种可移植的形式并没有增加额外开销。
~(~0 << n)
~0 << n将~0左移n位,并将最右边的n位用0填补。再使用~运算对它按位取反,这样就建立了最右边n位全1的屏蔽码。
二分查找的另一种形式:在while中只比较了两次,通过打印low mid high的值可以清楚的了解查找过程。
int binsearch(int x, int v[], int n)
{
int low, high, mid;
int times = 0;
low = 0;
high = n - 1;
mid = (low + high) / 2;
while (low <= high &&v[mid]!=x)
{
times += 1;
printf("%d times,low=%d,mid=%d,high=%d.\n",times,low,mid,high);
if (x < v[mid])
high = mid - 1;
else
low = mid + 1;
mid = (low + high) / 2;
}
printf("Last time %d times,low=%d,mid=%d,high=%d.\n",times+1,low,mid,high);
/* 虽然去掉了相等的测试,但如果有相等的情况,必然在v[mid]中 */
if (v[mid] == x)
return mid;
else
return -1;
}
运算符优先级
在switch语句中case的作用只是一个标号,因此,某个分之中的代码执行完后,程序将进入下一分支继续执行,除非在程序中显示地跳转。跳出switch语句最常用的方法是使用break与return 语句。break语句还可强制控制流从while for 和do while循环语句中立即退出。
数组与指针的区别
#include <stdio.h>
#include <string.h>
int GetSize(int data[])
{
return sizeof(data);
}
int main()
{
int data1[]={1,2,3,4,5};
int size1 = sizeof(data1);
int *data2 = data1;
int size2 = sizeof(data2);
int size3 = GetSize(data1);
printf("%d %d %d\n",size1,size2,size3);
return 0;
}
在64位系统运行结果为20 8 8,size1=4*5,size2是指针,64位的话指针占用8个字节,当数组作为函数参数进行传递时,数组自动退化为同类型的指针。所以size2和size3相同。
如果在xcode中编辑上述代码,会在代码位置出现如下提示:
Sizeof on array function parameter will return size of ‘int *’ instead of ‘int []’
二维数组作为函数参数
daytab[2][13]={
{0,31,28,31,30,31,30,31,31,30,31,30,31},
{0,31,29,31,30,31,30,31,31,30,31,30,31}
}
如果将二维数组作为参数传递给函数,那么函数的参数声明中必须指明数组的列数。数组的行数没有太大关系,函数调用时传递的是一个指针,它指向由行向量构成的一维数组,其中每个行向量是具有13个整型元素的一维数组,
在该例子中,传递给函数的是一个指向很多对象的指针,其中每个对象是由13个整型元素构成的一维数组。因此,如果将数组daytab作为参数传递给函数f,那么f的声明应该写成如下形式:
f(int daytab[2][13]){……}
也可以写成
f(int daytab[][13]){……}
由于数组的行数无关紧要,所以该声明还可以写成:
f(int (*daytab)[13]){……}
这种声明形式表明参数是一个指针,它指向具有13个整型元素的一维数组。因为方括号[]的优先级高于*的优先级,所以上述声明中必须使用圆括号,如果去掉圆括号
int *daytab[13]
则变成声明一个数组,该数组有13个元素,其中每个元素都是一个指向整型对象的指针。一般来说,除数组的第一维可以不指定大小外,其余各维必须明确指定大小。
goto语句与标号
goto语句最常见的用法是终止程序在某些深度嵌套的结构中的处理过程,例如一次跳出两层或多层循环。这种情况下使用break语句是不能达到目的的,它只能从最内层循环退到上一级的循环。
输出格式化控制
flag width.precision
The flag can be any of:
flag meaning
- left justify
+ always display sign
space display space if there is no sign
0 pad with leading zeros
# use alternate form of specifier
%#o adds a leading 0 to the octal value
%#x adds a leading 0x to the hex value
%#f or
%#e ensures decimal point is printed
%#g displays trailing zeros
作用域规则
名字的作用域指的是程序中可以使用该名字的部分。对于在函数开头声明的自动变量来说,其作用域是声明该变量名的函数。不同函数中声明的具有相同名字的各个局部变量之间没有任何关系。函数的参数也是这样的,实际上可以将它看作是局部变量。
如果函数没有参数,则使用void进行声明。