久芯网

C语言

 C语言是目前世界上流行、使用最广泛的高级程序设计语言。C语言对操作系统和系统使用程序以及需要对硬件进行操作的场合,用C语言明显优于其它高级语言,许多大型应用软件都是用C语言编写的。C语言具有绘图能力强,可移植性,并具备很强的数据处理能力,因此适于编写系统软件,三维,二维图形和动画它是数值计算的高级语言。
C语言

C语言结构体定义

语言中的“结构体”其实就相当于其他高级语言中的“记录”,结构体的定义方法如下:

 例如:

Struct student

{  int num;

  char name[20];

  char sex;

  int age;

  float  score;

  char addr[30];

};(注意最后的分号不能省略)。

 其中第一行的“student”是该结构体的名称,花括号里面的内容是结构体的成员名,这是声明结构体的一般形式。也可以在声明结构体的同时对它进行初始化,例如:

struct stu

{

        int num;

        char *name;

        char sex;

        float score;

}pupil[5]={

        {101,"Tom",'M',45.8},

        {102,"Mike",'M',62.5},

        {103,"Chris",'F',92.5},

        {104,"Rose",'F',87.6},

        {105,"Nate",'M',58.8}

};

 该代码中的“pupil[5]”称为结构体数组,它属于结构体变量,在定义该变量的同时对它进行了初始化操作。我们也可以先声明结构体,然后再对它进行初始化操作。

 例如:

#include <stdio.h>

int main()

{

        struct student

        {              

                char name[8];

                int age;

                char sex[4];

                char depart[20];

                float grade1,grade2,grade3;

        }a;

        float wage;

        char c='Y';

        if(c=='Y'||c=='y')     

        {

                printf("\nName:");

                scanf("%s", a.name);    

                printf("Age:");

                scanf("%d", &a.age);   

                printf("Sex:");

                scanf("%s", a.sex);

                printf("Dept:");

                scanf("%s", a.depart);

                printf("Grade1:");

                scanf("%f", &a.grade1); 

                printf("Grade2:");

                scanf("%f", &a.grade2);

                printf("Grade3:");

                scanf("%f", &a.grade3); 

                wage=a.grade1+a.grade2+a.grade3;

                printf("The sum of wage is %6.2f\n", wage);

        }

        return 0;

}

 该程序中定义了一个名为“student”的结构体,变量名为“a”,然后再后面“if”包含的符合语句中对该结构体进行初始化。在此,我们可以看出,对结构体的初始化,只能对它里面的每个成员分别初始化。

#include <stdio.h>

struct stu

{

        int num;

        char *name;

        char sex;

        float score;

}pupil[5]={

        {101,"Tom",'M',45.8},

        {102,"Mike",'M',62.5},

        {103,"Chris",'F',92.5},

        {104,"Rose",'F',87.6},

        {105,"Nate",'M',58.8}

};

void avg(struct stu *ps)

{

        int c=0,i;

        float ave,s=0;

        for(i=0;i<5;i++,ps++)

        {

                s+=ps->score;

                if(ps->score<60) c+=1;

        }

        printf("s=%.3f\n",s);

        ave=s/5;

        printf("average=%.3f\ncount=%d\n",ave,c);

}

int main()

{

        struct stu *ps;

        ps=pupil;

        avg(ps);

        return 0;

}

 

 此程序是关于结构体指针变量作函数参数,这样可以提高程序的运行效率,程序中我们定义了一个“stu”的结构体,变量名为“pupil[5]”,并对其进行了初始化,在主函数中定义了一个该结构体的指针ps,将pupil赋值给ps,当函数avg()调用该结构体时,用指针ps来传递pupil的地址,从而,提高了该程序的效率。

 结构体与指针的结合使用,可以有效的解决现实生活中的很多问题,因此C语言中的指针和结构体应该能够熟练的掌握。

C语言常犯错误

 语言的最大特点是:功能强、使用方便灵活。C编译的程序对语法检查并不象其它高级语言那么严格,这就给编程人员留下“灵活的余地”,但还是由于这个灵活给程序的调试带来了许多不便,尤其对初学C语言的人来说,经常会出一些连自己都不知道错在哪里的错误。看着有错的程序,不知该如何改起,本人通过对C的学习,积累了一些C编程时常犯的错误,写给各位学员以供参考。

 1.书写标识符时,忽略了大小写字母的区别。

 main()

 {

 int a=5;

 printf("%d",A);

 }

 编译程序把a和A认为是两个不同的变量名,而显示出错信息。C认为大写字母和小写字母是两个不同的字符。习惯上,符号常量名用大写,变量名用小写表示,以增加可读性。

 2.忽略了变量的类型,进行了不合法的运算。

 main()

 {

 float a,b;

 printf("%d",a%b);

 }

 %是求余运算,得到a/b的整余数。整型变量a和b可以进行求余运算,而实型变量则不允许进行“求余”运算。

 3.将字符常量与字符串常量混淆。

 char c;

 c="a";

 在这里就混淆了字符常量与字符串常量,字符常量是由一对单引号括起来的单个字符,字符串常量是一对双引号括起来的字符序列。C规定以“\”作字符串结束标志,它是由系统自动加上的,所以字符串“a”实际上包含两个字符:‘a'和‘\',而把它赋给一个字符变量是不行的。

 4.忽略了“=”与“==”的区别。

 在许多高级语言中,用“=”符号作为关系运算符“等于”。如在BASIC程序中可以写

 if (a=3) then …

 但C语言中,“=”是赋值运算符,“==”是关系运算符。如:

 if (a==3) a=b;

 前者是进行比较,a是否和3相等,后者表示如果a和3相等,把b值赋给a。由于习惯问题,初学者往往会犯这样的错误。

 5.忘记加分号。

 分号是C语句中不可缺少的一部分,语句末尾必须有分号。

 a=1

 b=2

 编译时,编译程序在“a=1”后面没发现分号,就把下一行“b=2”也作为上一行语句的一部分,这就会出现语法错误。改错时,有时在被指出有错的一行中未发现错误,就需要看一下上一行是否漏掉了分号。

 { z=x+y;

 t=z/100;

 printf("%f",t);

 }

 对于复合语句来说,最后一个语句中最后的分号不能忽略不写(这是和PASCAL不同的)。

 6.多加分号。

 对于一个复合语句,如:

 { z=x+y;

 t=z/100;

 printf("%f",t);

 };

 复合语句的花括号后不应再加分号,否则将会画蛇添足。

 又如:

 if (a%3==0);

 I++;

 本是如果3整除a,则I加1。但由于if (a%3==0)后多加了分号,则if语句到此结束,程序将执行I++语句,不论3是否整除a,I都将自动加1。

 再如:

 for (I=0;I<5;I++);

 {scanf("%d",&x);

 printf("%d",x);}

 本意是先后输入5个数,每输入一个数后再将它输出。由于for()后多加了一个分号,使循环体变为空语句,此时只能输入一个数并输出它。

 7.输入变量时忘记加地址运算符“&”。

 int a,b;

 scanf("%d%d",a,b);

 这是不合法的。Scanf函数的作用是:按照a、b在内存的地址将a、b的值存进去。“&a”指a在内存中的地址。

 8.输入数据的方式与要求不符。①scanf("%d%d",&a,&b);

 输入时,不能用逗号作两个数据间的分隔符,如下面输入不合法:

 3,4

 输入数据时,在两个数据之间以一个或多个空格间隔,也可用回车键,跳格键tab。

 ②scanf("%d,%d",&a,&b);

 C规定:如果在“格式控制”字符串中除了格式说明以外还有其它字符,则在输入数据时应输入与这些字符相同的字符。下面输入是合法的:

 3,4

 此时不用逗号而用空格或其它字符是不对的。

 3 4 3:4

 又如:

 scanf("a=%d,b=%d",&a,&b);

 输入应如以下形式:

 a=3,b=4

 9.输入字符的格式与要求不一致。

 在用“%c”格式输入字符时,“空格字符”和“转义字符”都作为有效字符输入。

 scanf("%c%c%c",&c1,&c2,&c3);

 如输入a b c

 字符“a”送给c1,字符“ ”送给c2,字符“b”送给c3,因为%c只要求读入一个字符,后面不需要用空格作为两个字符的间隔。

 10.输入输出的数据类型与所用格式说明符不一致。

 例如,a已定义为整型,b定义为实型

 a=3;b=4.5;

 printf("%f%d\n",a,b);

 编译时不给出出错信息,但运行结果将与原意不符。这种错误尤其需要注意。

 11.输入数据时,企图规定精度。

 scanf("%7.2f",&a);

 这样做是不合法的,输入数据时不能规定精度。

 12.switch语句中漏写break语句。

 例如:根据考试成绩的等级打印出百分制数段。

 switch(grade)

 { case 'A':printf("85~100\n");

 case 'B':printf("70~84\n");

 case 'C':printf("60~69\n");

 case 'D':printf("<60\n");

 default:printf("error\n");

 由于漏写了break语句,case只起标号的作用,而不起判断作用。因此,当grade值为A时,printf函数在执行完第一个语句后接着执行第二、三、四、五个printf函数语句。正确写法应在每个分支后再加上“break;”。例如

 case 'A':printf("85~100\n");break;

 13.忽视了while和do-while语句在细节上的区别。

 (1)main()

 {int a=0,I;

 scanf("%d",&I);

 while(I<=10)

 {a=a+I;

 I++;

 }

 printf("%d",a);

 }

 (2)main()

 {int a=0,I;

 scanf("%d",&I);

 do

 {a=a+I;

 I++;

 }while(I<=10);

 printf("%d",a);

 }

 可以看到,当输入I的值小于或等于10时,二者得到的结果相同。而当I>10时,二者结果就不同了。因为while循环是先判断后执行,而do-while循环是先执行后判断。对于大于10的数while循环一次也不执行循环体,而do-while语句则要执行一次循环体。

 14.定义数组时误用变量。

 int n;

 scanf("%d",&n);

 int a[n];

 数组名后用方括号括起来的是常量表达式,可以包括常量和符号常量。即C不允许对数组的大小作动态定义。

 15.在定义数组时,将定义的“元素个数”误认为是可使的最大下标值。

 main()

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

 printf("%d",a[10]);

 }

 C语言规定:定义时用a[10],表示a数组有10个元素。其下标值由0开始,所以数组元素a[10]是不存在的。

 16.初始化数组时,未使用静态存储。

 int a[3]={0,1,2};

 这样初始化数组是不对的。C语言规定只有静态存储(static)数组和外部存储(exterm)数组才能初始化。应改为:

 static int a[3]={0,1,2};

 17.在不应加地址运算符&的位置加了地址运算符。

 scanf("%s",&str);

 C语言编译系统对数组名的处理是:数组名代表该数组的起始地址,且scanf函数中的输入项是字符数组名,不必要再加地址符&。应改为:

 scanf("%s",str);

 18.同时定义了形参和函数中的局部变量。

 int max(x,y)

 int x,y,z;

 {z=x>y?x:y;

 return(z);

 }

 形参应该在函数体外定义,而局部变量应该在函数体内定义。应改为:

 int max(x,y)

 int x,y;

 {int z;

 z=x>y?x:y;

 return(z);

 }

C语言高效编程的几招

 编写高效简洁的C语言代码,是许多软件工程师追求的目标。本文就工作中的一些体会和经验做相关的阐述,不对的地方请各位指教。

 第1招:以空间换时间

 计算机程序中最大的矛盾是空间和时间的矛盾,那么,从这个角度出发逆向思维来考虑程序的效率问题,我们就有了解决问题的第1招——以空间换时间。

 例如:字符串的赋值。

 方法A,通常的办法:

 #define LEN 32

 char string1 [LEN];

 memset (string1,0,LEN);

 strcpy (string1,“This is a example!!”);

 方法B:

 const char string2[LEN] =“This is a example!”;

 char * cp;

 cp = string2 ;

 (使用的时候可以直接用指针来操作。)

 从上面的例子可以看出,A和B的效率是不能比的。在同样的存储空间下,B直接使用指针就可以操作了,而A需要调用两个字符函数才能完成。B的缺点在于灵活性没有A好。在需要频繁更改一个字符串内容的时候,A具有更好的灵活性;如果采用方法B,则需要预存许多字符串,虽然占用了大量的内存,但是获得了程序执行的高效率。

 如果系统的实时性要求很高,内存还有一些,那我推荐你使用该招数。

 该招数的变招——使用宏函数而不是函数。举例如下:

 方法C:

 #define bwMCDR2_ADDRESS 4

 #define bsMCDR2_ADDRESS 17

 int BIT_MASK(int __bf)

 {

 return ((1U << (bw ## __bf)) - 1) << (bs ## __bf);

 }

 void SET_BITS(int __dst, int __bf, int __val)

 {

 __dst = ((__dst) & ~(BIT_MASK(__bf))) \

 (((__val) << (bs ## __bf)) & (BIT_MASK(__bf))))

 }

 SET_BITS(MCDR2, MCDR2_ADDRESS, RegisterNumber);

 方法D:

 #define bwMCDR2_ADDRESS 4

 #define bsMCDR2_ADDRESS 17

 #define bmMCDR2_ADDRESS BIT_MASK(MCDR2_ADDRESS)

 #define BIT_MASK(__bf) (((1U << (bw ## __bf)) - 1) << (bs ## __bf))

 #define SET_BITS(__dst, __bf, __val) \

 ((__dst) = ((__dst) & ~(BIT_MASK(__bf))) \

 (((__val) << (bs ## __bf)) & (BIT_MASK(__bf))))

 SET_BITS(MCDR2, MCDR2_ADDRESS, RegisterNumber);

 函数和宏函数的区别就在于,宏函数占用了大量的空间,而函数占用了时间。大家要知道的是,函数调用是要使用系统的栈来保存数据的,如果编译器里有栈检查选项,一般在函数的头会嵌入一些汇编语句对当前栈进行检查;同时,CPU也要在函数调用时保存和恢复当前的现场,进行压栈和弹栈操作,所以,函数调用需要一些CPU时间。而宏函数不存在这个问题。宏函数仅仅作为预先写好的代码嵌入到当前程序,不会产生函数调用,所以仅仅是占用了空间,在频繁调用同一个宏函数的时候,该现象尤其突出。 D方法是我看到的最好的置位操作函数,是ARM公司源码的一部分,在短短的三行内实现了很多功能,几乎涵盖了所有的位操作功能。C方法是其变体,其中滋味还需大家仔细体会。

 第2招:数学方法解决问题

 现在我们演绎高效C语言编写的第二招——采用数学方法来解决问题。

 数学是计算机之母,没有数学的依据和基础,就没有计算机的发展,所以在编写程序的时候,采用一些数学方法会对程序的执行效率有数量级的提高。

 举例如下,求 1~100的和。

 方法E

 int I , j;

 for (I = 1 ;I<=100; I ++){

 j += I;

 }

 方法F

 int I;

 I = (100 * (1+100)) / 2

 这个例子是我印象最深的一个数学用例,是我的计算机启蒙老师考我的。当时我只有小学三年级,可惜我当时不知道用公式 N×(N+1)/ 2 来解决这个问题。方法E循环了100次才解决问题,也就是说最少用了100个赋值,100个判断,200个加法(I和j);而方法F仅仅用了1个加法,1次乘法,1次除法。效果自然不言而喻。所以,现在我在编程序的时候,更多的是动脑筋找规律,最大限度地发挥数学的威力来提高程序运行的效率。 第3招:使用位操作

 实现高效的C语言编写的第三招——使用位操作,减少除法和取模的运算。

 在计算机程序中,数据的位是可以操作的最小数据单位,理论上可以用“位运算”来完成所有的运算和操作。一般的位操作是用来控制硬件的,或者做数据变换使用,但是,灵活的位操作可以有效地提高程序运行的效率。举例如下:

 方法G

 int I,J;

 I = 257 /8;

 J = 456 % 32;

 方法H

 int I,J;

 I = 257 >>3;

 J = 456 - (456 >> 4 << 4);

 在字面上好像H比G麻烦了好多,但是,仔细查看产生的汇编代码就会明白,方法G调用了基本的取模函数和除法函数,既有函数调用,还有很多汇编代码和寄存器参与运算;而方法H则仅仅是几句相关的汇编,代码更简洁,效率更高。当然,由于编译器的不同,可能效率的差距不大,但是,以我目前遇到的MS C ,ARM C 来看,效率的差距还是不小。相关汇编代码就不在这里列举了。

 运用这招需要注意的是,因为CPU的不同而产生的问题。比如说,在PC上用这招编写的程序,并在PC上调试通过,在移植到一个16位机平台上的时候,可能会产生代码隐患。所以只有在一定技术进阶的基础下才可以使用这招。

 第4招:汇编嵌入

 高效C语言编程的必杀技,第四招——嵌入汇编。 “在熟悉汇编语言的人眼里,C语言编写的程序都是垃圾”。这种说法虽然偏激了一些,但是却有它的道理。汇编语言是效率最高的计算机语言,但是,不可能靠着它来写一个操作系统吧?所以,为了获得程序的高效率,我们只好采用变通的方法 ——嵌入汇编,混合编程。

 举例如下,将数组一赋值给数组二,要求每一字节都相符。

 char string1[1024],string2[1024];

 方法I

 int I;

 for (I =0 ;I<1024;I++)

 *(string2 + I) = *(string1 + I)

 方法J

 #ifdef _PC_

 int I;

 for (I =0 ;I<1024;I++)

 *(string2 + I) = *(string1 + I);

 #else

 #ifdef _ARM_

 __asm

 {

 MOV R0,string1

 MOV R1,string2

 MOV R2,#0

 loop:

 LDMIA R0!, [R3-R11]

 STMIA R1!, [R3-R11]

 ADD R2,R2,#8

 CMP R2, #400

 BNE loop

 }

 #endif

 方法I是最常见的方法,使用了1024次循环;方法J则根据平台不同做了区分,在ARM平台下,用嵌入汇编仅用128次循环就完成了同样的操作。这里有朋友会说,为什么不用标准的内存拷贝函数呢?这是因为在源数据里可能含有数据为0的字节,这样的话,标准库函数会提前结束而不会完成我们要求的操作。这个例程典型应用于LCD数据的拷贝过程。根据不同的CPU,熟练使用相应的嵌入汇编,可以大大提高程序执行的效率。 虽然是必杀技,但是如果轻易使用会付出惨重的代价。这是因为,使用了嵌入汇编,便限制了程序的可移植性,使程序在不同平台移植的过程中,卧虎藏龙,险象环生!同时该招数也与现代软件工程的思想相违背,只有在迫不得已的情况下才可以采用。切记,切记。

 使用C语言进行高效率编程,我的体会仅此而已。在此以本文抛砖引玉,还请各位高手共同切磋。希望各位能给出更好的方法,大家一起提高我们的编程技巧。

C语言声明学习之初级篇

 C语言为我们定义了四种基本数据类型:整型,浮点型,指针以及聚合类型(数组和结构体等),在此基础上,我们就可以声明变量。我们平时经常说定义一个某种类型的变量,其实这样说不确切,应该说是声明变量。

 变量声明的基本形式是:

 说明符(一个或多个) 声明表达式列表

 比如说:int a, b, c, d;

 C语言中对指针的声明比较有代表性,我们来看一下:

 比如声明一个指向int型的指针a:int *a;

 这个语句表示表达式*a产生的结果类型是int,而我们又知道*操作符执行的是间接访问操作,所以可以推断a肯定是一个指向int的指针。

 C语言在本质上是一种自由形式的语言,它给了程序员很大的空间,我们同样可以这样写:int* a,这个声明与int *a时一个意思,而且似乎更为清楚,a被声明为类型为int*的指针(实则不然),这会诱导我们这样声明三个指向int型的指针:

 int* a, b, c;

 也许你会很自然的以为这条语句把三个变量a、b、c都声明为指向整型的指针,但是事实上我们被它的形式愚弄了,星号实际上是表达式*a的一部分,只对这个标识符有用,a是一个指针,但是b和c都只是普通的整型而已,要声明三指针,这样写是可以的:

 int *a, *b, *c;

 从这个简单的例子我们可以看出C语言的声明规则多么具有迷惑性,呵呵,这也是C语言饱受批*的地方之一,但这决定与语言本身的设计哲学,我们无法改变,要想用好C语言,我们必须掌握它的语法规则。

 我们再看一个例子:

 int fun();

 我们都知道它把f声明为一个函数,它的返回值是一个整数。

 如果这样写:

 int *fun();

 要想推断出它的含义,我们必须知道*fun()是如何求值的。首先执行的是函数调用操作符(),因为它的优先级高于间接访问操作符*,所以fun是一个函数,它的返回值类型是一个指向整型的指针。

 再看一个更为有趣的声明:

 int (*fun)();

 这个声明有两对括号,每对括号的含义不同。第二对括号是函数调用操作符,但是第一对只起到聚组的作用。它导致间接访问在函数调用之前进行,使fun是一个函数指针,它所指向的函数返回一个整型值。

 那么现在这个声明应该很容易分析出来了

 int *(*fun)();

 fun还是一个函数指针,只是所指向的函数返回的是一个整型指针。

 先写到这里,对C语言的声明之旅才刚刚开始,下回我们将在中级篇里讨论更有趣的话题!

C语言声明学习之中级篇

 C语言的声明存在的最大的问题就是你无法以一种人们所习惯的自然方式从左到右阅读一个声明,程序员必须记住特殊的规则才能推断出int *p[3]到底是一个int类型的指针数组还是一个指向int数组的指针。

 对于这样一个声明,我们应该如何分析?

 ——————int f()[];

 首先,f是一个函数,其次,它的返回值是一个整型数组。貌似就是这样啊,但实际上,这个例子隐藏着一个陷阱,因为这个声明是非法的,呵呵,在我们的C语言里,函数只能返回变量值,不能返回数组。

 还有一个让人颇费脑筋的声明:

 ——————int f[] ();

 这里,f应该是一个数组,数组的元素类型是返回值为整型的函数。请不要对它看似正确的表面所迷惑,其实这个声明也是非法的!因为数组元素必须具有相同的长度,但是不同的函数显然可能具有不同的长度吧,呵呵。

 在被C语言迷幻的声明形式欺骗两次之后,现在是不是有些草木皆兵了?让我们乘热打铁,再看一个声明:

 ——————int (*f[]) ();

 请你分析一下它的含义?首

标签: 接地电阻 贴片电容 整流电路 放大电路 精密电阻 led封装 热电阻 安规电容 安全芯片 功率电感
会员中心 微信客服
客服
回到顶部