您的位置:首页 > 编程语言 > C语言/C++

c语言注意事项

2014-02-18 15:04 363 查看
1、getchar()函数的使用方法

getchar()返回值为int型,如下使用方法不正确

[cpp] view
plaincopy

#include <stdio.h>

int main()

{

char tmp;

while( (tmp=getchar())!=EOF )

{

printf("%c",tmp);

}

return 0;

}

当EOF的宏定义超出char型数据所表示的范围时,这样就会出错,这种错误很难预料。

char的取值范围究竟是[-128,127]还是[0-255]取决于编译器。GCC编译器中的char表示signed char。

所以应该将tmp的类型改成int 型,防止出现错误。

2、全局变量的使用方法

cfaq.h

[cpp] view
plaincopy

#ifndef _CAFQ_H

#define _CAFQ_H

//DEFINE(int, ex);

extern int ex;

void print();

#endif

cfaq.c

[cpp] view
plaincopy

#include "cfaq.h"

int ex;

void print()

{

printf("%d\n",ex);

}

main.c

[cpp] view
plaincopy

#include <stdio.h>

#include "cfaq.h"

int ex=3;

int main(int argc,char **argv)

{

print();

return 0;

}

[cpp] view
plaincopy

3、extern关键字使用

声明变量extern修饰的表示全局变量

而修饰函数时有无extern修饰无实质差别

4、使用typedef声明复杂的声明

下面首先使用typedef声明大小为N的指向字符的指针的函数的指针的数组

typedef char *pc;//定义一个字符指针

typedef pc fpc();//定义一个返回字符指针的函数

typedef fpc *pfpc;//定义一个指向fpc()函数的函数指针

typedef pfpc fpfpc();//返回上述函数指针的函数

typedef fpfpc *pfpfpc;//上述函数的指针

pfpfpc a
;//定义一个上面函数指针的数组

相当于下面一句话

char *(*(a
)())();

5、main()函数的正确定义

main()函数必须声明为int main()或int main(void)或int main(int argc,int **argv)

不能定义为void main(){ }以消除无返回值警告,但是它可能导致与调用者期待的返回顺序不能的返回顺序

6、char a[]="String"和char *a="String"初始化字符串的区别

第一种方法初始化之后可以修改a[i]的值,第二种无法修改a[i]的值,其存储空间是只读的,当修改时出现错误。

7、C中参数传递的方式

C中确实没有按引用传递的参数传递方式。

严格地讲, C 总是按值传递。你可以自己模拟按引用传递, 定义接受指针的函数, 然后在调用时使用 & 操作符。事实上, 当你向函数传入数组 (传入指针的情况参见问题 6.4 及其它) 时, 编译器本质上就是在模拟按引用传递。但是 C 没有任何真正等同于正式的按引用传递或 C++ 的引用参数的东西。另一方面, 类似函数的预处理宏可以提供一种“按名称传递”的形式。

8、有关i++的定义

int i=3;

printf("%d",(i++)*(i++));我的编译器输出结果是9,而不是12。原因尽管后缀自加和后缀自减操作符 ++ 和 -- 在输出其旧值之后才会执行运算,但这里的“之后”常常被误解。没有任何保证确保自增或自减会在输出变量原值之后和对表达式的其它部分进行计算之前立即进行。也不能保证变量的更新会在表达式 “完成”之前的某个时刻进行。本例中, 编译器选择使用变量的旧值相乘以后再对二者进行自增运算。

9、*p++的含义是自增指针p,还是自增p的内容

*p++和*(p++)等价,++和--比前缀的一目操作运算符优先级高,它的含义是自增p并返回p增加前地址的内容。所以如果自己比较混淆,干脆使用括号消除自己的歧义。

10、左值与右值

顾名思义,左值就是只能出现在=号的左边,可以被赋值;右值是只能出现在=号的右端,不能被赋值也不能自增;

例如下面一个char *的指针恰好指向一个int型数据,要想对其做自增运算。

那么下面的代码正确吗?

((int *)p)++

答案是否定的,由于(int *)p是经过类型转换得到的,由于类型转换就是将部分二进位看作其他类型,并做相应的对待。数据类型转换所产生的是一个右值(R-value),故不能对其进行修改和自增操作。

下面是GCC 4.5下的编译出错信息



1、如何使用指针调用函数

如下例

[cpp] view
plaincopy

#include <stdio.h>

void func()

{

printf("asdfg\n");

}

void (*fp)();

int main(int argc,char **argv)

{

fp=func;

(*fp)();

return 0;

}

我们认为一个函数指针必须前面加上*后面加一个额外的括号从而转换成一个真正的函数,就如上例。

但是下面的方法也可以正确执行

[cpp] view
plaincopy

#include <stdio.h>

void func()

{

printf("asdfg\n");

}

void (*fp)();

int main(int argc,char **argv)

{

fp=func;

fp();

return 0;

}

由于函数的调用其实就是通过指针实现,而真正的函数总是饮食的退化为指针。ANSI C解释*符号不再需要,但依然兼容。

2、strcpy,strncpy,strdup的用法和实现

char * strcpy ( char * destination, const char * source );

功能:复制字符串,复制source至destination,返回目的串的地址。

源长>目标长,将源中长度等于目标长的部分复制到目的串

源长<目标长,将源中全部字符复制到目的串,不包括最后的'\0'

[cpp] view
plaincopy

/***********************************************

* 字符串复制函数

* 源长>目标长,将源中长度等于目标长的部分复制到目的串

* 源长<目标长,将源中全部字符复制到目的串,不包括最后的'\0'

* ************************************/

char * strcpy(char * destination,const char * source)

{

if(destination == NULL || source == NULL) return NULL;

char * tmp=destination;

for(;*source != '\0';destination++,source++)

{

*destination = *source;

}

return tmp;

}

例如

char* src="how are you ?";

char des[20]="ABCDEFGHIJKLMNOPQRS;

strcpy(des,src);

结果des为



char * strncpy ( char * destination, const char * source, size_t num );

功能:复制字符串,复制源中定长的字符串到目标字符串

目标长>指定长>源长,则将源长全部拷贝到目标长,自动加上'\0'

指定长<源长,则将源长中按指定长度拷贝到目标字符串,不包括'\0'

指定长>目标长,出错ERROR

[cpp] view
plaincopy

/**

* strncpy - Copy a length-limited, %NUL-terminated string

* @dest: Where to copy the string to

* @src: Where to copy the string from

* @count: The maximum number of bytes to copy

*

* The result is not %NUL-terminated if the source exceeds

* @count bytes.

*

* In the case where the length of @src is less than that of

* count, the remainder of @dest will be padded with %NUL.

*

*/

char *strncpy(char *dest, const char *src, size_t count)

{

char *tmp = dest;

while (count) {

if ((*tmp = *src) != 0)

src++;

tmp++;

count--;

}

return dest;

}

char *strdup(char *src)

功能:将src字符串复制到新的位置,返回值即为目的串地址,其目的串地址是由malloc()分配,使用完毕须用free()释放。

[cpp] view
plaincopy

char *strdup(char *src)

{

char *tmp = NULL;

if(src!=NULL && (tmp=malloc(strlen(src)+1)) !=NULL )

strcpy(tmp,src);

return tmp;

}

3、NULL(空指针)到底是什么?

每个指针都有一个特殊的值,空NULL,这与其他对象或函数的地址都不相同,也就是说在程序中通过&(取地址运算符)永远无法得到NULL地址。

当一个指针类型的变量被赋予0值,那么编译器在编译的时候会将其作为NULL指针,即在指针初始化、比较、赋值的时候NULL可用0代替。即自己定义

#define NULL 0

如果NULL要以函数参数传递的时候,编译器可能不知道这是指针类型的变量而把其当作是常数0看待,这是可以进行强制类型转换(char *)0来使编译器将其作为NULL来使用。即

#define NULL ((void *)0)

所以空指针的使用注意

*在源码中使用空指针常数时,则使用0或NULL

*在函数的参数传递时如果使用空指针,则使用NULL或相应的类型指针0((type *)0)

4、if(p)这样判断是否是空指针正确吗?

if(p)相当于if(p != 0)

完全合法,但是一些人认为这样的风格不好。

在指针上下文中NULL和0是完全等价的。

5、char a
和char *a的区别

例如:

char a[6]="world";

表示定义一个有6个存储空间的连续地址空间,首地址是a,故a[i]可以修改。

char *p="world";

表示定义一个指针,该指针指向一个有6个连续地址空间的首地址。

如下图示意:



6、和5不同,为什么在函数参数中指针和数组可以等价

[cpp] view
plaincopy

void func(char a[])

{

........

}



[cpp] view
plaincopy

void func(char *a)

{

........

}

相同,这种等价只局限于形参的传递,别的地方并不适用。

7、和5不同的是,C语言中“指针与数组等价”该如何理解?

数组的定义中,如果一个数组变量出现在=的左边,即作为左值出现,那么它立刻蜕化为一个相应类型的指针。

8、如何给二位数组动态分配内存?

[cpp] view
plaincopy

int dmalloc(int **arg)

{

int cols=6;

int rows=6;

arg=malloc( rows*sizeof(int *) );

int i,j;

for(i=0;i<cols;i++)

{

arg[i]=malloc( cols*sizeof(int) );

}

for(i=0;i<rows;i++)

{

for(j=0;j<cols;j++) arg[i][j]=i*j,printf("%d ",arg[i][j]);

printf("\n");

}

for(i=0;i<cols;i++) free(arg[i]);

free(arg);

}



9、二维数组参数的传递的方法

int arr[rows][cols];

[cpp] view
plaincopy

void func(int arr[][cols])

{

.............

}

或者

[cpp] view
plaincopy

typedef ap *arr

void func(int ap[cols])

{

.............

}

10、当数组以参数的方式传递后,参数内sizeof()无法获得数组的大小

由于数组以形参传递后,实质是传递的一个指针,首地址,所以在函数内通过sizeof获得的不是数组的大小,而是指针的大小。

1、自己实现itoa(int)函数,由整型转换成字符串。

大家看看下面的是否有错?

[cpp] view
plaincopy

/*************************************

* 整型转换成字符串

* *********************************/

char *itoa(int n)

{

#if 0

int tmp = n;

int cnt = 1;

while(tmp != 0)

{

tmp /= 10;

cnt++;

}

char *buf;

buf = (char *)malloc(cnt * sizeof(char));

#else

char buf[20];

#endif

sprintf(buf,"%d",n);

return buf;

}

运行结果标明得不到正确的结果。

但将char buf[20]修改成static char buf[20]结果正确。

这是由于函数内static修饰变量表示该变量是静态变量,在函数被调用过程中一直保持不变。如果不加static关键字,那么在函数体内定义的变量在函数调用完毕其内存就释放,所以无法得到正确的结果。

也可以根据整型变量的位数来动态分配内存

[cpp] view
plaincopy

/*************************************

* 整型转换成字符串

* *********************************/

char *itoa(int n)

{

#if 1

int tmp = n;

int cnt = 1;

while(tmp != 0)

{

tmp /= 10;

cnt++;

}

char *buf;

buf = (char *)malloc(cnt * sizeof(char));

#else

char buf[20];

#endif

sprintf(buf,"%d",n);

return buf;

}

但是要注意字符串使用完毕后记得使用free()函数来释放其内存。

所以说,函数的返回指针必须是静态分配的缓冲区或是由调用者传入的缓冲区或者是由被调用者使用malloc()函数动态申请的内存。

2、sizeof(char)的值是什么?

我们经常会看到下面的代码

[cpp] view
plaincopy

char *p;

p=(char *)malloc(strlen(string)+1);

strcpy(p,string);

而我们经常写的时候是

p=(char *)malloc((strlen(string)+1)*sizeof(char) );

这说明,sizeof(char)的值恒为1,但是sizeof('a')的值为2,这是由于'a'表示一个整型的ASCII码,为int型。

3、malloc()函数分配的内存必须由free()函数释放,且只能释放一次。

下面注意释放由malloc()分配内存的单链表的释放过程。注意使用的技巧。

[cpp] view
plaincopy

LNode *listp,*nextp;

for(listp = base;listp != NULL;listp = nextp)

{

nextp = listp->next;

free(listp);

}

4、calloc()和malloc()的区别

其实calloc(m,n)实质上就是

p=malloc(m*n);

memset(p,0,m*n);

malloc()和calloc()申请的内存都可以使用free()来释放。

5、如果有人给你提出这个问题:假设两个整型变量,你如何不用额外的临时存储变量从而达到交换两个变量的值的目的

[cpp] view
plaincopy

int a=23;

int b=45;

a ^= b;

b ^= a;

a ^= b;

6、C语言中注释是不能嵌套的,因为/*会和最近的*/匹配,那么当要想注释掉大段包含注释的代码的时候需要使用什么方法呢?

对了,就是预处理

#ifdef XXX #else #endif

或者干脆使用

#if 0 #else #endif

7、C语言中assert()函数的使用和作用

assert.h中有assert()函数的宏定义

[cpp] view
plaincopy

#define assert(test) ( test ? (void)0 : _Assert(__FILE__":"_STR(__LINE__)"" #test) )

作用个是在测试表达式为假(=0)时输出一条错误信息。

该函数只在程序调式时有用,在正常运行时不会执行,其是否起作用是通过宏来控制

关闭assert() #define NDEBUG

打开assert() #undef NDEBUG

8、给一个日期,计算是星期几的简洁代码。由Tomohiko Sakamoto 提供

[cpp] view
plaincopy

int dayofweek(int y, int m, int d) /* 0 = Sunday */

{

static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};

y -= m < 3;

return (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7;

}

9、使用#define 预处理定义输出语句TRACE()便于程序的调试

[cpp] view
plaincopy

#define TRACE(var,fmt) printf("TRACE: " #var " = " #fmt "\n",var)

其中变量前的#用于和字符常量的连接。使用的一个实例。下面加入需要输出一个整型变量a

TRACE(a,%d);即可。

10、printf()的使用技巧>>如何让程序根据输出的结果自动实现输出的合理的宽度,而不是认为的做如下规定

printf("%5d",a);

下面这种用法可以达到预想的效果

printf("%*d",width,a);

1、scanf()输入注意

当你使用scanf("%d",&a),然后用gets()读取下一行的一个字符串时,调用后好像gets()函数没有执行。

原因:这是由于scanf()函数不处理回车换行符。这样以来gets()函数读取的只是回车换行符。所以好像时gets()函数没有执行。

解决方法:可以在scanf()函数后插入一个getchar()函数来吃掉那个回车换行符。

2、scanf()的缓冲区问题

为什么说scanf()函数有问题呢?就因为它适用于结构化,格式相对整齐的数据输入。

例如下面的例子

[cpp] view
plaincopy

#include <stdio.h>

int main(int argc,char **argv)

{

char a;

while(1)

{

scanf("%c",&a);

printf("a=%c\n",a);

}

return 0;

}

当输入连续asdfg时运行结果如下:



这里可以看出scanf()读取数据到缓冲区,当缓冲区不空时从缓冲区截取,当缓冲区为空时,再读一组数据到缓冲区。所以为保证安全

*可以在每次scanf()后加一个清空缓冲区的语句fflush(stdin),但是这不是所有的编译器都支持的,VC6.0支持,但是GCC不支持。

*使用scanf()的替代功能函数。可以先用fgets()读入一整行,然后用字符串处理函数进行字符串处理(strtok(),strtol(),atoi())

3、printf()和sprintf()函数

就像scanf()函数一样,这两个函数也存在缓冲区的问题,例如输出一个字符串,当你不可预测字符串的长度时,可能存在字符串的过长导致缓冲区溢出,从而影响其他内存区。

如下例子:

[cpp] view
plaincopy

int main(int argc,char **argv)

{

char a[11]={0};

const char *p="12345678901234";

sprintf(a,"%s",p);

printf("a= %s\n",a);

return 0;

}

当p的长度大于系统给a分配的内存区,这样就会导致系统错误。



*当我们知道字符串的结构,我们可以使用sprintf(a,"%10s",p);防止超越内存区。

*使用snprintf()函数原型int snprintf(char *restrict buf, size_t n, const char * restrict format, ...);

例如

[cpp] view
plaincopy

int main(int argc,char **argv)

{

char a[11]={0};

const char *p="12345678901234";

snprintf(a,sizeof(a),"%s",p);

printf("a= %s\n",a);

return 0;

}



4、GCC中qsort()函数使用方法

使用方法

*对int,char,double,float等数组排序方法

[cpp] view
plaincopy

#include <stdlib.h>

#define MY_TYPE int

static int cmp(const void *a,const void *b)

{

return *(MY_TYPE *)a > *(MY_TYPE *)b ? 1 : -1;

}

int va[10]={6,3,2,4,7,1,9,8,5,0};

int main(int argc,char **argv)

{

int i;

for(i=0;i<10;i++)

{

printf("%d ",va[i]);

}

printf("\n");

qsort(va,10,sizeof(va[0]),cmp);

for(i=0;i<10;i++)

{

printf("%d ",va[i]);

}

printf("\n");

return 0;

}



*对结构体按一个关键字排序

[cpp] view
plaincopy

#include <stdlib.h>

#define MY_TYPE _st

typedef struct _st

{

int va;

char c;

};

struct _st st[10];

static int cmp(const void *a,const void *b)

{

return (*(struct MY_TYPE *)a).va > (*(struct MY_TYPE *)b).va ? 1 : -1;

}

int main(int argc,char **argv)

{

int i;

for(i=0;i<10;i++)

{

st[i].va=10-i;

}

printf("\n");

qsort(st,10,sizeof(st[0]),cmp);

for(i=0;i<10;i++)

{

printf("%d ",st[i].va);

}

printf("\n");

return 0;

}



比较函数也可以写成如下形式

[cpp] view
plaincopy

static int cmp(const void *a,const void *b)

{

return ((struct MY_TYPE *)a)->va > ((struct MY_TYPE *)b)->va ? 1 : -1;

}

*对结构体多级排序

[cpp] view
plaincopy

int cmp( const void *a , const void *b )

{

struct In *c = (In *)a;

struct In *d = (In *)b;

if(c->x != d->x) return c->x - d->x;

else return d->y - c->y;

}

*对字符串数组排序

[cpp] view
plaincopy

#include <stdlib.h>

#define LEN 30+1

char str[4][LEN];

static int cmp(const void *a,const void *b)

{

return strcmp((char *)a,(char *)b);

}

int main(int argc,char **argv)

{

int i;

strcpy(str[0],"abaa");

strcpy(str[1],"baaa");

strcpy(str[2],"baaaa");

strcpy(str[3],"aasasasaa");

printf("\n");

qsort(str,4,sizeof(str[0]),cmp);

for(i=0;i<4;i++)

{

printf("%s\n",str[i]);

}

printf("\n");

return 0;

}



5、程序中条件语句中的double值的相等判断

double a,b;

.....

if( a == b )

这样不对,应该使用

[cpp] view
plaincopy

#include <math.h>

......

if (fabs(a - b)<= epsilon * fabs(a))

6、C语言中取整的方法

我们知道float或double化为整型可以使用

(int)(a+0.5);但是这样对负数不合适。

应该使用这样的方法

(int)(x<0 ? x - 0.5 : x + 0.5);

7、C中数学方法获得 π 的值

可以自己定义也可以使用数学函数计算

4*atan(1.0) 或 acos(-1.0)

8、如何定义参数可变的函数

[cpp] view
plaincopy

#include <stdio.h>

#include <stdlib.h>

#include <stdarg.h>

void __print(const char *fmt,...)

{

va_list argp;

printf("INFO: ");

va_start(argp,fmt);

vprintf(fmt,argp);

va_end(argp);

printf("\n");

}

int main(int argc,char **argv)

{

__print("%s","asddasda");

return 0;

}



注意参数个数至少是一个。

9、为什么有的人判断语句的写法如下

if(0 == x)

而不是if( x==0 )

当然这两个都没有错误。这是由于防止"=="写成"="。

当把变量放在前面,“==”写成“=”编译器就会报错,不至于引发不应该出现的BUG

10、如何实现控制台的“转动的小棒”来显示程序的执行进度。

这里需要使用fflush(stdout);来清空输出缓冲区。

[cpp] view
plaincopy

void print1(int ca)

{

switch(ca)

{

case 0:printf("\\");break;

case 1:printf("/");break;

case 2:printf("-");break;

}

}

int main(int argc,char **argv)

{

__print("%s","asddasda");

int i;

for(i=0;i<10;i++)

{

printf("\r");

print1(i%3);

fflush(stdout);

sleep(1);

}

return 0;

}

转自:http://blog.csdn.net/yming0221/article/details/7247075
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: