一、数据类型
一些常见的数据类型在这里我也就不多做介绍了。
1._Bool类型
这部分我想先来说说_Bool类型。_Bool类型是由C99引入的,用于表示布尔值,即逻辑true(真)与false(假)。因为C用值1表示true,用值0表示false,所以_Bool类型实际上也是一种整数类型。只是原则上它仅仅需要1位来进行存储。
而在一般地,c的while、if等判断语句中,只有0被认为是假,任何非零的值都认为是真。
另外C99还提供一个stdbool.h的头文件。包含这个头文件可以使用bool来代替_Bool,并把true和false定义成值1和0的符号常量。
如果系统还不支持_Bool类型,则可以使用int来代替_Bool。
二、可移植类型:inttypes.h
C99提供了一个可选的名字集合,以确切地描述有关信息。例如int16_t表示一个16位有符号整数类型,uint32_t表示一个32位无符号整数类型。
要使用这些名字对于程序有效,应当在程序中包含inttypes.h头文件。因为,与int不同,uint32_t不是关键字。
三、ctype.h系列字符函数
ANSI C有一系列标准的函数可以用来分析字符;ctype.h头文件包含了这些函数的原型。这些函数接受一个字符作为参数,如果该字符属于某特定的种类则返回一个非零值。
例:
#include <stdio.h>
#include <ctype.h>
int main()
{
char ch;
while((ch = getchar()) != '/n')
{
if(isalpha(ch))
putchar(ch + 1);
else
putchar(ch);
}
putchar(ch);
return 0;
}
运行结果:
注意:映射函数并不改变原始的参数,它们只返回改变后的值。也就是说,下列语句是不改变ch的值,
tolower(ch),但可以 ch = tolower(ch)
关于一些其他函数可以查阅相关手册,本人推荐C-Free的C/C++语言参考帮助文档。
四、系统定义的明显常量
C头文件limits.h和float.h分贝提供有关整数类型的大小限制的详细信息。每个文件都定义了一系列应用您的实现的明显常量。
五、sizeof运算符和size_t类型
size_t不是一个新类型。相反,与可移植类型相同,使根据标准类型定义的。
六、getchar()
1.使用输入缓冲:
首先看个例子:
#include <stdio.h>
int main( )
{
int guess = 1;
printf("Pick an integer from 1 to 100. I will try to guess ");
printf("it./nRespond with a y if my guess is right and with");
printf("/nan n if it is wrong./n");
printf("Uh...is your number %d?/n", guess);
while (getchar() != 'y') /* get response, compare to y */
printf("Well, then, is it %d?/n", ++guess);
printf("I knew I could do it!/n");
return 0;
}
运行结果:
注意该程序在每次您输入n时都进行两次猜测。这中间所发生的事情是改程序读去n响应并把它看作是您对1的否定,然后读取换行字符并把它看作是您对2的否定。
一种解决方法是使用while循环来丢弃输入行的其余部分,包括换行符。这种方法还能够把诸如no和no way这样的响应得简单的n响应一样看待。即在循环跳出前加虾面的语句:
while(getchar () != ‘/n’)
{
continue;
}
2.混合输入数字和字符
getchar()读取每个字符,包括空格、制表符和换行符。而scanf()在读取数字时则跳过空格、制表符和换行符。
void display(char cr, int lines, int width);
int main( )
{
int ch; /* character to be printed */
int rows, cols; /* number of rows and columns */
printf("Enter a character and two integers:/n");
while ((ch = getchar()) != '/n')
{
scanf("%d %d", &rows, &cols);
display(ch, rows, cols);
printf("Enter another character and two integers;/n");
printf("Enter a newline to quit./n");
}
printf("Bye./n");
return 0;
}
void display(char cr, int lines, int width)
{
int row, col;
for (row = 1; row <= lines; row++)
{
for (col = 1; col <= width; col++)
putchar(cr);
putchar('/n'); /* end line and start a new one */
}
}
运行结果,这里就不给出了,可以自己运行一下。顺便提到一句,以上程序都是在C-Free4.1的mingw2.95下编译,调试的。
这个程序的问题还是出在换行符上。因为scanf()函数将该换行符留在输入队列中。与scanf()不同,getchar()并不跳过换行符。
七、复合文字
C99新增了复合文字(compound literal)。文字是非符号常量。开发C99标准委员会认为,如果有能够表示数足和结构内容的复合文字,那么在编写程序时将更为方便。
例:
(int [2]){10, 20}; //一个复合文字
(int [ ]){10, 20, 40}; //也是一个复合文字
由于复合文字没有名称,因此不可能在一个语句中创建他们,然后再另一个语句中使用。而是必须在创建他们的同时通过某种方法来使用他们,一种方法是使用指针来保存其位置。
int *pt1;
pt1 = (int [2]){10, 20};
请注意这个文字常量被标示为一个int数组。与数组名相同,这个常量同时代表首元素的地址。因此可以用它给一个指向int的指针赋值。随后就可以使用这个指针。
例:
#include <stdio.h>
#define COLS 4
int sum2d(int ar[][COLS], int rows);
int sum(int ar[], int n);
int main(void)
{
int total1, total2, total3;
int * pt1;
int (*pt2)[COLS];
pt1 = (int [2]) {10, 20};
pt2 = (int [2][COLS]) { {1,2,3,-9}, {4,5,6,-8} };
total1 = sum(pt1, 2);
total2 = sum2d(pt2, 2);
total3 = sum((int []){4,4,4,5,5,5}, 6);
printf("total1 = %d/n", total1);
printf("total2 = %d/n", total2);
printf("total3 = %d/n", total3);
return 0;
}
int sum(int ar[], int n)
{
int i;
int total = 0;
for( i = 0; i < n; i++)
total += ar[i];
return total;
}
int sum2d(int ar[][COLS], int rows)
{
int r;
int c;
int tot = 0;
for (r = 0; r < rows; r++)
for (c = 0; c < COLS; c++)
tot += ar[r][c];
return tot;
}
运行结果:
|
八、从snprintf( )引发的一些讨论
许多现有的代码调用的是sprintf,但sprintf不检查目标缓冲区是否溢出。相反,snprintf要求其第二个参数指定目标缓冲区的大小,因此可确保缓冲区不溢出。
snprintf相对较晚才加到ANSI C标准,在称为ISO C99的版本中引入。几乎所有厂商都把它作为标准C函数库德一部分加以提供,而且含有许多免费的版本可用。
值得注意的是,许多网络入侵是由黑客通过发送数据,导致服务器对sprintf的调用溢出其缓冲区而发生的。你必须小心使用的函数还有gets、stract和strcpy,通常应调用fgets、strncat和strncpy函数代替。更好的选择是使用后来才引入的函数strlcat和strlcpy,它们确保结果是终结得到的字符串。