c语言基础解疑

这几天看了一些有关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,结构体中是指向函数的指针,这里我们只能感叹指针的强大。



Previous     Next
zhing /
Published under (CC) BY-NC-SA in categories C语言  tagged with C语言