国嵌C语言总结(1-5)

国嵌一:

什么是数据类型?

        数据类型是固定内存大小的别名,是创建变量的模子;

变量的本质?

  • 变量是一段实际连续存储空间的别名;
  • 程序通过变量来申请并命名存储空间;
  • 通过变量的名字可以使用存储空间。

Linux命令行:gcc.test.c 编译     ./a.out 运行

数据类型:

  • 自定义类型:typedef定义类型
  • 创建变量:TYPE,name
  • 通过打印语句证明本质:printf
#include <stdio.h>
int main(void)
{
    char c=0;
    short s=0;
    int i=0;
    printf("%d,%d\n",sizeof(char),sizeof(c));
    printf("%d,%d\n",sizeof(short),sizeof(s));
    printf("%d,%d\n",sizeof(int),sizeof(i));
}

1,1
2,2
4,4

typedef int INT32;
typedef unsigned char BYTE;
typedef struct _demo

{
    short s;
    BYTE b1;
    BYTE b2;
    INT32 i;
}DEMO;

int main(void)
{
    INT32 i32;
    BYTE byte;
    DEMO d;
    printf("%d,%d\n",sizeof(INT32),sizeof(i32));
    printf("%d,%d\n",sizeof(BYTE),sizeof(byte));
    printf("%d,%d\n",sizeof(DEMO),sizeof(d));

}

4,4
1,1
8,8

国嵌二:

        auto为C语言局部变量的默认属性,编译器默认所有的局部变量都是auto,在栈上分配内存;

         register指明将变量存储在寄存器中,只是请求寄存器变量,但是不一定成功;需要的目的是:由于从寄存器中取值比内存中快的多,因此可以应用到讲究性能的实时系统中。特别是函数经常被调用时;register的变量必须是CPU寄存器可以接受的值,不能用&运算符获取变量的地址。

        static指明变量的静态属性,同时具有作用域限定符的意义;修饰的局部变量存储在程序的静态工作区;static还可以作为文件作用域标识符,即修饰的全局变量的作用域只是在声明的文件中,修饰的函数作用域只是声明的文件之中。局部变量的生命中周期是函数的运算期,但是static修饰的局部变量是整个生命周期。

补充:

        auto 存储类是所有局部变量默认的存储类,auto 只能用在函数内。

        register 存储类用于定义存储在寄存器中而不是 RAM 中的局部变量。这意味着变量的最大尺寸等于寄存器的大小(通常是一个词),且不能对它应用一元的 '&' 运算符(因为它没有内存位置)。定义 'register' 并不意味着变量将被存储在寄存器中,它意味着变量可能存储在寄存器中,这取决于硬件和实现的限制。

        static 存储类指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。因此,使用 static 修饰局部变量可以在函数调用之间保持局部变量的值。static 修饰符也可以应用于全局变量。当 static 修饰全局变量时,会使变量的作用域限制在声明它的文件内。全局声明的一个 static 变量或方法可以被任何函数或方法调用,只要这些方法出现在跟 static 变量或方法同一个文件中。

        extern 存储类用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。当您使用 'extern' 时,对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置。当有多个文件且定义了一个可以在其他文件中使用的全局变量或函数时,可以在其他文件中使用 extern 来得到已定义的变量或函数的引用。可以这么理解,extern 是用来在另一个文件中声明一个全局变量或函数。

#include <stdio.h>
int main()
{
    auto int i = 0;
    register int j = 0;
    static int k = 0;    
    return 0;
}

4,4

1,1

8,8

#include <stdio.h>
int g=0;
int m=0;
void f1()
{
    int i=0;
    i++;
    printf("%d,",i);                //局部变量,退出后释放
}
void f2()
{
    static i=0;                    //静态局部变量,初始化一次,不释放
    i++;
    printf("%d,",i);
}
int main()
{
    auto int i = 0;
    register int j = 0;
    static int k = 0;
    printf("%0x\n",&i);
    //printf("%0x\n",&j);         //要求寄存器变量j的地址
    printf("%0x\n",&k);
    for(i=0;i<5;i++)
    {
        f1();                    //在栈上占用空间,每次运行完就释放
    }
    printf("\n");
    for(i=0;i<5;i++)
    {
        f2();           //在静态存储区占用空间,只会被初始化一次,每次运行完后保留上一次的值
    }
    return 0;
}

1,1,1,1,1

1,2,3,4,5

  • //auto int g=0;//错误,文件作用域中g的声明指定了auto
  • //register int m=0;//m的寄存器名无效,全局变量不可能是寄存器变量

另一个文件如果是int test_g=1;则可以打印test_g得值;

int test_f1()
{
    return test_fun();
}

如果是static int test_g=1;则不可以打印test_g得值

static int test_g=1;
int test_fun()
{
    return test_g;
}

如果是static int test_fun()则不可以打印test_g得值,如果再加一个函数,可以打印test_g得值。

  •  外部变量和静态变量由编译程序给予隐含的初始值0;
  • 局部变量的初始化每进入函数便初始化一次。
  • 外部或静态变量仅在编译时初始化一次。
  • 自动变量或寄存器变量只能显示初始化,否则将有不确定的值。
  • 外部数据的说明,如果带有初始化项,则当成一个定义。

        auto 普通局部栈变量,是自动存储,这种对象会自动创建和销毁 ,建议这个变量要放在堆栈上面,调用函数时分配内存,函数结束时释放内存。一般隐藏auto默认为自动存储类别。我们程序都变量大多是自动变量。

#include <stdio.h>
int main(void) 
{ 
    auto int i = 9; /* 声明局部变量的关键字是 auto; 因可以省略, 几乎没人使用 */ 
    printf("%d\n", i); 
    getchar(); 
    return 0; 
}

        Register变量:动态和静态变量都是存放在内存中,程序中遇到该值时用控制器发指令将变量的值送到运算器中,需要存数再保存到内存中。如果频繁使用一个变量,比如一个函数体内的多次循环每次都引用该局部变量,我们则可以把局部变量的值放到CPU的寄存器中,叫寄存器变量。不需要多次到内存中存取提高效率。但是只能局部自动变量和形参可以做寄存器变量。在函数调用时占用一些寄存器,函数结束时释放。不同系统对register要求也不一样,比如对定义register变量个数,数据类型等限制,有的默认为自动变量处理。所以在程序一般也不用。

#include <stdio.h>
#include <>
#define TIME 1000000000 
int m, n = TIME; /* 全局变量 */ 
int main(void) 
{ 
    time_t start, stop; 
    register int a, b = TIME; /* 寄存器变量 */ 
    int x, y = TIME; /* 一般变量 */ 
    time(&start); 
    for (a = 0; a b; a++); 
    time(&stop); 
    printf("寄存器变量用时: %ld 秒\n", stop - start); 
    time(&start); 
    for (x = 0; x y; x++); 
    time(&stop); 
    printf("一般变量用时: %ld 秒\n", stop - start); 
    time(&start); 
    for (m = 0; m n; m++); 
    time(&stop); 
    printf("全局变量用时: %ld 秒\n", stop - start); 
    return 0; 
}

输出结果:寄存器变量用时: 1 秒 一般变量用时: 8 秒 全局变量用时: 9 秒

static 可以用来修饰局部变量,全局变量以及函数。在不同的情况下 static 的作用不尽相同。

(1)修饰局部变量

#include void fun() 
{ 
    static int a=1; 
    a++; 
    printf("%d\n",a); 
} 
int main(void) 
{ 
    fun(); 
    fun(); 
    return 0; 
}

程序执行结果为: 2 3

(2)修饰全局变量

//有file1.c 
int a=1; 
file2.c 
#include <stdio.h>
extern int a; 
int main(void) 
{ 
    printf("%d\",a); 
    return 0; 
}

则执行结果为 1

但是如果在 file1.c 中把 int a=1 改为 static int a=1;那么在file2.c是无法访问到变量a的。原因在于用static对全局变量进行修饰改变了其作用域的范围,由原来的整个工程可见变为本源文件可见。

(3)修饰函数

        用static修饰函数的话,情况与修饰全局变量大同小异,就是改变了函数的作用域。

国嵌三:

if语句需要注意:

  • bool型变量应该直接出现在条件中,不能进行比较;
  • 普通变量和0值比较时,0值应该出现在比较符号的左边,防止出现赋值的情况;
  • float型变量不能直接进行0值比较,需要定义精度;

         在输入时:使用非零值表示真;零值表示假。在输出时:真的结果是1,假的结果是0。当在一个需要布尔值的地方,也就是其它类型转化为布尔类型时,比如 if 条件判断中的的条件;“输出”的意思是:程序的逻辑表达式返回的结果,也就是布尔类型转化为其他类型时,比如 a==b的返回结果,只有0和1两种可能。

 switch语句需要注意:

  • switch语句对应单个条件多个分值的情况,
  • 每个case语句必须要有break,否则会出现分支重叠;
  • case语句中的值只能是整形或者字符型。

if语句可以安全从功能上替代switch语句,但是switch语句无法替代if语句。

void f1(int i)
{
    if( i < 6 )
    {
        printf("Failed!\n");
    }
    else if((6<i)&&(i<=8))//范围的话switch无法替代if
    {
        printf("Good!\n");
    }
    else
    {
        printf("Perfect!\n");
    }
}
void f2(char i)
{
    switch(i)
    {
        case 'c':
            printf("Compile\n");
        break;
        case 'd':
            printf("Debug\n");
        break;
        case 'o':
            printf("Object\n");
        break;
        case 'r':
            printf("Run\n");
        break;
        default:
            printf("Unknown\n");
        break;
    }
}
int main()
{
    f1(5);
    f1(9);
    f2('o');
    f2('d');
    f2('e');
}

failed  prefect  object  debug  unknown

循环语句分析:

  • do语句先执行后判断,循环体至少执行一次;
  • while语句先判断后执行,循环体可能不执行;
  • for语句先判断后执行,相比于while更简便;
#include <stdo.h>
int f1(int n)
{
    int ret = 0;
    int i = 0;
    for(i=1; i<=n;i++)
    {
        ret += i;
    }
    return ret;
}
int f2(int n)
{
    int ret = 0;
    while( (n > 0) && (ret += n--) );
    return ret;
}
int f3(int n)
{
    int ret = 0;
    if( n > 0 )
    {
        do//至少执行一次
        {
            ret += n--;
        }while( n );
    }
    return ret;
}
int main()
{
    printf("%d\n", f1(10));
    printf("%d\n", f2(10));
    printf("%d\n", f3(10));
}

55 55 55

  • break语句是跳出循环;
  • continue语句是跳出本次循环,进入下次循环;
  • switch语句不能使用continue,因为switch语句本身不存在循环。
#include <stdio.h>
int func(int n)
{
    int i = 0;
    int ret = 0;
    int* p = (int*)malloc(sizeof(int) * n);//分配动态内存
    do
    {
        if( NULL == p ) 
            break;                //不能用return 0;如果返回值不是0,会出现内存错误
        if( n < 0 ) 
            break;                //不能用return 0;如果返回值不是0,会出现内存错误
        for(i=0; i<n;i++)
        {
            p[i] = i;
            printf("%d,", p[i]);
        }
        ret = 1;
    }while(0);
    free(p);                    //释放内存
    return ret;
}
int main()
{
    if( func(10) )
    {
        printf("OK");
    }
    else
    {
        printf("ERROR");
    }
}

0,1,2,3,4,5,6,7,8,9 OK

国嵌四:

goto禁用。

goto虽好不使用,因为它使得程序的控制流难以跟踪,使程序难以理解和难以修改。:

#include <stdio.h>
void func(int n)
{
    int* p = NULL;
    if( n < 0 )
    {
        goto STATUS;
    }
    p = malloc(sizeof(int) * n);
    STATUS:
    p[0] = n;
}
int main()
{
    f(1);
    f(-1);
    return 0;
}

void的意义

        C语言没有定义void究竟是多大的内存的别名,无法在内存中裁剪出void对应的变量。如果函数没有返回值,没有参数,就应该声明为void型;且在C语言中void不占据内存。

void*指针应该注意的问题:

  • 在C语言中只有相同类型的指针才可以进行相互赋值;
  • void*指针作为左值用于接收任意类型的指针;
  • void*作为右值赋值给其他指针,需要强制类型的转换。

void* my_memset(void* p,char v,int size)
{
    void* ret=p;
    char* dest=(char*)p;
    int i=0;
    for(i=0;i<size;i++)
    {
        dest[i]=v;
    }
    return 0;
}
int main()
{
    int a[5]={1,2,3,4,5};
    int i=0;
    for(i=0;i<5;i++)
    {
        printf("%d,",a[i]);
    }
    printf("\n");
    my_memset(a,0,sizeof(a));
    for(i=0;i<5;i++)
    {
        printf("%d,",a[i]);
    }
    return 0;
}

1,2,3,4,5

0,0,0,0,0

extern 中隐藏的意义:

  • extern用于声明外部定义的变量和函数,
  • extern告诉编译器用C语言的方式编译。

(由于c++编译器和一些其他编译器会按照自己的编译方式运行,而通过extern可以命令编译器以标准的C语言方式进行编译)

test.c
/*
extern "C"
{
    int add(int a, int b)
    {
        return a + b;
    }
}
*/
extern int g;
extern int get_min(int a, int b);
int main()
{
    printf("%d\n",g);
    printf("%d\n",get_min(1,5));
    return 0;
}
// test2.c
int g = 100;
int get_min(int a, int b)
{
    return (a < b) ? a : b;
}

gcc test.c test2.c        ./a.out

100,1

sizeof不是函数:

  • sizeof是编译器的内置指示符,不是函数,
  • sizeof用于计算相应实体所占据的内存大小;
  • sizeof的值在编译期就已经确定了
int main()
{
    int a;
    printf("%d\n", sizeof(a));
    printf("%d\n", sizeof a);//充分证明sizeof不是函数
    printf("%d\n", sizeof(int));
    return 0;
}

4,4,4

国嵌五:

const修饰变量:

  • const修饰的变量是只读的,其本质还是变量,
  • const会占据一定的内存空间;
  • 本质上const只对编译器有用,在运行时无用;
int main(void)
{
    const int cc=1;
    printf("%d\n",cc);
    cc=3;            //只读类型,不能修改,故报错
    printf("%d\n",cc);
}
int main(void)
{
    const int cc=1;
    int *p=(int *)&cc;
    printf("%d\n",cc);
    *p=3;
    printf("%d\n",cc);

}

1,3

const修饰数组:

  • const修饰的数组是只读的;
  • const修饰的数组空间不可被改变,通过指针可以更改。

const修饰指针:(左数右指)

  • 当const出现在*左边的时候,指针指向的数据为常量,
  • 当const出现在*右边的时候,指针本身为常量。
  • const int *p; //p可变,p指向的内容不可变;
  • int const *p; //p可变,p指向的内容不可变;
  • int *const p; //p不可变,p指向的内容可变;
  • const int *const p; //p和p指向的内容都不可变;
#include <stdio.h>
int main(void)
{
    int i=0;
    //const int *p=&i;
    //*p=3; //只读,不能改变,报错
    //int const*p=&i;
    //*p=3; //同上
    int* const p=&i;
    *p=3; //编译通过
    //int* const p=&i;
    //p=NULL; //只读,不能改变,报错
    //const int* const p=&i;
    //*p=3; //同上
    //p=NULL; //同上
}

const修饰函数参数和返回值:

  • const修饰函数表示在函数体内不希望改变参数的值。
  • const修饰函数返回值表示返回值不可改变,多用于返回指针的情形。
#include <stdio.h>
const int* func()
{
    static int count=0;
    count++;
    return &count;
}
int main(void)
{
    int i=0;
    const int* p=func();//没有const就会有警告,因为返回的地址是不可改变的
    printf("%d\n",*p);
}

1

volatile的理解:

  • volatile可以理解为编译器警告指示字,
  • volatile用于告诉编译器必须每次去内存中取变量的值;
  • volatile主要修饰被多个线程访问的变量,
  • volatile也可以修饰可能被未知因数更改的变量。

         const和volatile是可以同时修饰一个变量的  const只是表示变量只读 不能出现在赋值号左边  防止程序“意外”修改   并且编译器一定会做优化 不会每次去内存取值 这个时候如果外部事件 如中断服务程序 改了这个变量的内存值  那么由于编译器优化就不会出有反应 这样会导致错误 加上volatile就告诉编译器 不要做任何优化  并且每次都去内存取值  而且这个变量不可以当左值使用 .

问题1:const与volatile是否可以修饰同一个变量?

        如果一个变量不会被本程序改变,通常可能给它加上const,但如果该变量可能被其他程序改变而本程序又在检测这个变量的值,就需要给它加上volatile,于是变量就同时有volatile和const了,这个时候i具有const和volatile的双重属性。

 问题2:const volatile int i=0;这个是时候i具有什么属性?编译器如何处理这个变量?

        i变量不可以在编译过程中被程序代码修改,同时编译器不得对i进行优化编译。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
入式Linux视频教程全套2011新版,强烈推荐!!! 视频1.iso -学习方法与课程体系介绍(学前必看) -学习方法介绍.avi -入式课程体系.pdf -入式Linux学习方法.pdf -课程1-入式入门体验班(上) -第1天(入式系统概述) -体验入门班-1-1(入式系统概述).avi -体验入门班-1-2(ARM概述).avi -体验入门班-1-3(入式Linux概述).avi -体验入门班-1-4(2440开发板介绍).avi -体验入门班-1-5(软硬件环境搭建).avi -第2天(开发板快乐体验) -体验入门班-2-1(开发板系统安装).avi -体验入门班-2-1(开发板系统安装-Jlink方式).avi -体验入门班-2-1(开发板系统安装-并口方式).avi -体验入门班-2-2(裸机程序体验).avi -体验入门班-2-3(QT系统体验).avi -体验入门班-2-4(Android系统体验).avi 视频2.iso -课程1-入式入门体验班(下) -第3天(Linux系统体验) -体验入门班-3-1(Linux定制安装).avi -体验入门班-3-2(Linux命令).avi -体验入门班-3-3(VI使用).avi -体验入门班-3-4(Linux系统管理).avi -体验入门班-3-5(Shell编程).avi -体验入门班-3-6(Qcd功能演示).avi -体验入门班-3-7(必修实验).avi -课程2-入式Linux应用开发班 -第1天(编程基础) -应用班-1-1(GCC程序编译).avi -应用班-1-2(GDB程序调试).avi -应用班-1-3(makefile工程管理).avi -应用班-1-4(必修实验).avi -第2天(文件时间编程) -应用班-2-1(系统调用方式访问文件).avi -应用班-2-2(库函数访问文件).avi -应用班-2-3(时间编程).avi -应用班-2-4(必修实验).avi -第3天(多进程程序设计) -应用班-3-1(进程控制原理).avi -应用班-3-2(进程控制程序设计).avi -应用班-3-3(必修实验).avi -第4天(进程间通讯) -应用班-4-1(进程间通讯概述).avi -应用班-4-2(管道通讯).avi -应用班-4-3(信号通讯).avi -应用班-4-4(共享内存通讯).avi -应用班-4-5(必修实验).avi -第5天(进程间通讯) -应用班-5-1(消息队列).avi -应用班-5-2(信号量).avi -应用班-5-3(必修实验).avi -第6天(进程间通讯) -应用班-6-1(线程基础).avi -应用班-6-2(多线程程序设计).avi -应用班-6-3(必修实验).avi -第7天(网络编程) -应用班-7-1(Linux网络概述).avi -应用班-7-2(Linux网络编程基础).avi -应用班-7-3(TCP程序设计).avi -应用班-7-4(UDP网络程序设计).avi -应用班-7-5(并发服务器).avi -应用班-7-6(必修实验).avi 视频3.iso -课程3-ARM系统进阶班 -第1天(ARM系统开发基础) -ARM系统精讲班-1-1(ADS集成开发环境).avi -ARM系统精讲班-1-2(ARM工作模式).avi -ARM系统精讲班-1-3(ARM寄存器).avi -ARM系统精讲班-1-4(ARM寻址方式).avi -ARM系统精讲班-1-5(ARM汇编指令详解).avi -第2天(ARM系统开发基础) -ARM系统精讲班-2-1(ARM汇编指令详解).avi -ARM系统精讲班-2-2(ARM伪指令).avi -ARM系统精讲班-2-3((混合编程).avi -ARM系统精讲班-1-8(必修实验).avi -第3天(LED驱动程序设计) -ARM系统精讲班-2-1(GPIO).avi -ARM系统精讲班-2-2(LED控制程序设计).avi -ARM系统精讲班-2-3((LED闪烁).avi -第4天(LED与按键驱动程序设计) -ARM系统精讲班-4-1(ARM中断控制系统).avi -ARM系统精讲班-4-2(按键控制程序设计).avi -ARM系统精讲班-2-6(必修实验).avi -第5天(定时器与时钟驱动程序设计) -ARM系统精讲班-5-1(ARM时钟体系).avi -ARM系统精讲班-5-2(定时器驱动程序设计).avi -课程4(入式LINUX内核驱动进阶班-上) -第1天(内核开发基础) -内核驱动进阶班-1-1(Linux内核简介).avi -内核驱动进阶班-1-2(Linux内核源代码).avi -内核驱动进阶班-1-3(Linux内核配置与编译).avi -内核驱动进阶班-1-4(Linux内核模块开发).avi -内核驱动进阶班-1-5(必修实验).avi -第2天(U-Boot移植) -内核驱动进阶班-2-1(入式linux系统架构).avi -内核驱动进阶班-2-2(交叉工具链).avi -内核驱动进阶班-2-3(Bootloader介绍).avi -内核驱动进阶班-2-4(U-Boot介绍).avi -内核驱动进阶班-2-5(U-Boot命令).avi -内核驱动进阶班-2-6(U-Boot启动流程).avi -内核驱动进阶班-2-7(Uboot移植).avi -内核驱动进阶班-2-8(必修实验).avi 视频4.iso -课程4(入式LINUX内核驱动进阶班-下) -第3天(内核开发基础) -内核驱动进阶班-3-1(入式linux内核制作).avi -内核驱动进阶班-3-2(根文件系统制作).avi -内核驱动进阶班-3-3(内核启动流程).avi -内核驱动进阶班-3-4(入式文件系统).avi -内核驱动进阶班-3-5(必修实验).avi -第4天(内存管理子系统) -内核驱动进阶班-4-1(Linux内存管理).avi -内核驱动进阶班-4-2(进程地址空间).avi -内核驱动进阶班-4-3(内核地址空间).avi -内核驱动进阶班-4-4(Linux内核链表).avi -内核驱动进阶班-4-5(Linux内核定时器).avi -内核驱动进阶班-2-7(Uboot移植).avi -内核驱动进阶班-2-8(必修实验).avi -第5天(进程管理子系统) -内核驱动进阶班-5-1(LINUX进程控制).avi -内核驱动进阶班-5-2(LINUX进程调度).avi -内核驱动进阶班-5-3(Linux系统调用).avi -内核驱动进阶班-5-4(Proc文件系统).avi -内核驱动进阶班-5-5(内核异常分析).avi -内核驱动进阶班-5-6(必修实验).avi -第6天(简单字符设备驱动程序设计) -内核驱动进阶班-6-1(LINUX驱动程序介绍).avi -内核驱动进阶班-6-2(字符设备驱动程序设计).avi -内核驱动进阶班-6-3(字符设备驱动程序实例分析).avi -内核驱动进阶班-6-4(竞争与互斥).avi -内核驱动进阶班-6-5(必修实验).avi -第7天(高级字符设备驱动程序设计) -内核驱动进阶班-7-1(Ioctl设备控制).avi -内核驱动进阶班-7-2(内核等待队列).avi -内核驱动进阶班-7-3(阻塞型字符设备驱动).avi -内核驱动进阶班-7-4(Poll设备方法).avi -内核驱动进阶班-7-5(自动创建设备文件).avi -内核驱动进阶班-7-6(必修实验).avi

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值