这几天看了一些有关c基础的内容,以前觉得c语言学的还可以,但是看过别人整理的一些东西后,很多细节以前自己 都是很模糊的,现在把这几天学习的一些东西做一下整理。
if语句
在写c代码的过程中if语句是最常用的判断语句,但是很多时候我们容易对其产生误用,我们重点看一下浮点数和 指针的判“零”,对于整数,布尔值没什么好说的。
浮点数
const float EPSINON = 0.00001;//浮点数的需要精度 if ((x>=-EPSINON) && (x<=EPSINON)){ }
这里不要写成x==0.0.
指针
对于指针判“零”很简单,就是: void *p; if(p==NULL){ printf("指针p被初始化为NULL"); }
但是懂得c语言的朋友一下子就能看出上述语句的错误,void *p虽然被申明,但是不会被自动初始化为NULL, 它的指向是未知的(俗话说“野指针”),所以if条件下的语句不会被执行。
字符串
字符串是c语言中最常用的数据类型之一,一般声明字符串有两种方法:
char s[20]="hello world";或者 char *s="hello world";
但是这两种声明方法绝不能混淆,前一种是在栈区开辟了一个指针数组,后一种方法是在常量区初始化一个字符串 常量"hello world",然后在栈区有一个指针指向它。下面的例子更能反应本质:
char *GetString(void){
char p[] = "hello world";//改成char *p= 能正确输出
return p; // 编译器将提出警告
}
int main(){
char *str = NULL;
str = GetString(); // str 的内容是垃圾
cout<< str << endl;
return 0;
}
上面的代码尝试将栈区的内存返回,当然是不可能的,因为栈区随着函数的结束被收回了。
数组退化为指针
我们先来看一个典型的例子:
int main(){
char s[100]="hello wolrd";
printf("%d\n",sizeof(s));
test(s);
}
void test(char s[100]){
printf("%d\n",sizeof(s));
}
上面的代码中main里面的sizeof(s)为100,但是test函数中sizeof(s)大小为4,所以在传递数组为参数的过程中,被 退化为指针,所以在书写函数的时候,传递一个数组的指针显然更方便。
malloc使用
在使用malloc分配堆区的空间时,我们常常要使用指针转型和assert断言。
char *s = (char *)malloc(100*sizeof(char));
assert(s!=NULL);
上面的代码在堆中申请了一个大小为100的字符数组,但是malloc返回的是void 类型的指针,所以需要强制转化为 char 型指针(不是必须的,编译器可能会帮你完成),我们都知道指针不只有地址信息,还有类型信息,理解这一点 是玩转指针的基础。
在使用malloc的时候很有可能申请不到堆空间,编译器一般会尝试5次,如何还是失败则返回NULL,所以良好的习惯 是在malloc之后一点要对申请的结果加以判断。
void 类型的指针就是没有类型,它可以转化为任何其它类型的指针,这里面有点面向对象型语言中template模板的 意思,所以在《c interfaces and implementtations》中使用void 来实现各种c的STL。
.h头文件
我们在编写.h头文件的时候往往会困惑如何写才是最恰当的,下面给出了模板:
#ifndef GRAPHICS_H // 防止graphics.h 被重复引用
#define GRAPHICS_H
#include <math.h> // 引用标准库的头文件
...
#include “myheader.h” // 引用非标准库的头文件
...
#define MAX(a,b) (a>b?a:b)
...
extern void Function1(…); // 外部函数声明
...
#endif
我们最好不要再头文件中书写函数的实现,这涉及到安全保护的问题,比如说在linux中一般把.h头文件和二进制 链接文件暴露给用户,程序员可以使用他们来使用库。上一段代码中extern表示函数在外部(一般.c文件中)实现, 当然extern不是必须的,表示函数为一个全局函数。
const
const是c语言中的常量标识符,它与#define共同完成c语言中常量的定义,我们经常在函数调用中看到它,如:
char *strcpy(char *strDest, const char *strSrc){
assert((strDest!=NULL) && (strSrc !=NULL));
char *address = strDest;
while( (*strDest++ = * strSrc++) != ‘\0’ )
NULL ;
return address ;
}
上面的代码是strcpy函数的一个简单实现,其中strSrc是常量字符指针,我们不能通过strSrc来改变其指向的内容, 即这个字符串是只读的,所以它与普通变量的const类型有区别。有char *类型的放回值是为了方便地使用该函数连 接。
函数指针
我们在使用函数调用的时候,函数名就是指向该函数的指针,那么我们能不能像面向对象那样在结构体中定义函数 方法呢,答案是肯定的。
static int cal(int x,int y){
return x+y;
}
struct T{
int a,b;
int (*cal)(int x,int y)
};
int main(){
struct T *t = malloc(*t);
t->cal = cal;t->a=1;t->b=2;
(*t->cal)(t->a,t->b);
}
上面的代码会输出结果3,结构体中是指向函数的指针,这里我们只能感叹指针的强大。