C 编程总结

来源:互联网 发布:篮球分析软件 编辑:程序博客网 时间:2024/04/30 02:50
 

C 编程基础

选择变量标准:够用就好,不是越大越好
int x;占用两个字节
long y; 占4个字节的空间

短整型常量:-32768~32767 认为它是int型,它可以赋值给int型和long int类型

长整型:long int

如果某一计算机的C版本确定的short int 与int 型数据在内存中占据的长度相同,则它的表示范围与int相同。因此一个
int型的常量也同时是一个short int 型的常量,可以赋值给int型或short int 型变量

常量中无 unsigned型,但是一个非负值的整常量可以赋给unsigned型的整常量,只要它的范围不超过变量的表示范围。
长整型常量:数据后加一个l或L

例子:
main()
{
 int x;
x=10*9*8*7*4*....*1;
printf("%d",x);
}
Linux下可以正常运行,windows出现溢出。
int 换成long int ,%d换成%ld 结果会如何?

单精度float:内存占4个字节 有效数字:6~7
双精度double:内存8个字节    有效数字:15~16
long double :内存16个字节   有效数字:18~19
用sizeof来判断当前数据类型所占用的内存空间,不同系统所占用的空间不同

字符变量:字符变量用来存放字符常量,注意一个字符变量只能存放一个字符,用一个字节来存放一个字符,如 char

name='a'; name='\034'; ASCII码 name='\n'; name='\x65';c=65;c=0101;c=0x41;

int a=1,b=2;定义变量并初始化。

数组:在内存分配若干连续空间给数组 a[n] n必须为常量表达式
a[5]={0,1,2,3,4};
static int a[]={1,2,3};
static 型数组不进行初始化时,如果是数值类型数值默认为0,如果是字符型数组默认为空字符'\0'(ASCII为0的字符)
auto 型数组不进行初始化时,编译器不为其自动指定初始值。其初始值为系统分配给数组各元素的内存单元原来的值,这

个值是不可预知的。

二维数组:元素在内存排列顺序为按行存放。
a[2][3] 表示有两个一维数组 ,每个数组长度为3. a[0][0] a[0][1] a[0][2]
分行初始化: static int a[3][4]={{1,2,3,4},{...},{...},{...}}数组的数组,一行行赋值

字符串处理函数
#include"string.h"
char * strcpy(char *dest,const char *src); 字符串拷贝,从前往后复制。将src赋值到字符数组dest,返回被复制的

字符串
src可以是字符数组名,字符串常量或字符指针变量。
若dest字符指针变量,要给该指针变量赋初值。dest应该用数组名,不用&取地址
例子:
static char a[10]="abcdefghi";
b[]="happy"; strcpy(a,b); 内存情况happy\0ghi\0
用printf输出的时候,碰到\0则表示字符串结束,则为happy
字符串长度函数 unsigned int strlen(const char *str) 字符串实际长度,不包括'\0';
char s1[5];
gets(s1);//从键盘获取字符串

函数默认返回整型
1)auto型:有形式参数、函数内变量、分程序变量,进入程序自动分配内存,不长期占用内存
2)static 长期占用内存  分全局静态和局部静态
3)register:使用频率高的变量定义为register型,可以提高运行速度
4)extern:引用 extern 类型 变量名; 如果某一个模块要用到另外一个模块 文件的全局变量,要用extern说明

 

指针的用法(重点和难点)
1.数据在内存中的存储方式:
按数据类型给在内存为其分配一定数量的存储单元(字节);
2.内存单元的地址
内存单元的编号,与变量名对应

int a,b; char c; float x;  a,b为内存空间的外号

4.变量的”直接访问“方式
按变量的地址(即变量名)存取变量值的方式。
5.变量的间接访问
将变量的地址放在另一个内存单元,先到另一个内存单元中取得变量的地址,再由
变量的地址找到变量并进行数据存取。
int i;
int *pointer=&i;
变量pointer为指针变量,存放的是变量i的内存地址,我们说pointer指向i
6.指针的概念
一个变量的地址称为该变量的指针
7.指针变量
专门用于存储其他变量地址的变量。
指针与指针变量的区别,就是变量值与变量的区别。
指针变量的值:另一个变量在内存当中的地址
指针变量的类型:和所指变量的类型一致。
指针变量的名字:名字和普通变量相同
指针变量的赋值:指针变量名=&变量名  &为取地址符
方式二: 指针变量=另一个指针变量
对指针进行自加,根据数据类型进行移动
int *p int 占两个字节空间,p++移动两个字节
数组的名字就是一个指针变量,指向数组的首地址


指针的指针
   指向指针变量的指针
   定义形式:类型 **变量名
   变量名为指针的指针,指向另外一个指针
   例如:int i,*p,**q;
   i=30;
   p=&i;
   q=&p;
 
                             指针与函数
1)指针做函数的参数(*) 传值调用:形参变化,实参不受影响。 传引用调用:实参和形参都变化
2)指针函数 (返回类型为指针类型)
    返回值是指针类型的函数,称为指针函数。
    定义形式: 类型标识符 *函数名(参数表)
    int *a(int x,float y); a 是函数名,调用它后得到一个指向整型数据的指针
3)指向函数的指针(函数在内存也有块地址,通过指针来引用函数,指向函数的首地址)
    函数名代表函数的入口地址
    定义方式:类型 (*指针变量名)();
    eg:int (*p)();
    int max(){};
    int (*p)();
    p=max; 使用是使p指向max函数的入口地址,可以通过(*p)(参数)来调用max函数。

    指针与数组
1)指向一维数组的指针
   定义:类型 *指针变量名
   数组名代表数组的首地址
   指向数组元素的指针变量的赋值;指针变量=数组某一元素的地址;
   eg:int a[10],*p;
      p=a;  或p=&a[0]; 两者结果一样  a+1或p+1指向第二个元素。
       a+9或p+9指向第10个元素,不仅仅移动一个位置,是移动一个数组元素的位置
      或p=&a[4];
    p=p+1;新p指向数组的下一个元素。 意味着p比原来的P的地址多d个字节(d为一个数组元素所占的字节数)
    引用一个数组元素:a[i]形式; 指针法:*(a+i)或*(p+i);(重点和难点)
    p++合法,但是a++不合法(a是数组名,代表数组的首地址,是常数地址)
    要注意指针变量的当前值
    x=*p++;(尽量少出现这种形式)
    *与++是同级运算,等价于先计算*p,为x赋值a[0];p在自增1,指向下一个元素a[1];
    *(p++)与*(++p);
    *(p++)先取*p的值,后使p加1
    *(++p)先使p加1,后取*p的值
   (*p)++ 是内容加1,p不变
   
2)指向多维数组的指针
  eg: int a[3][4],*p=a,*q=a[0];
    表示形式                含义                       地址
    a,p                    二维数组第0行首地址         2000
    a[0],*(a+0),*a,q       第0行0列元素地址            2000
    a+1,q+1                第1行首地址                 2008
    a[1],*(a+1),q[1]        第一行第0列地址            2008
    a[i]+j,*(a+i)+j,
    &a[i][j],p[i]+j,*(p+i)+j  第i行第j列地址
    *(对应的地址)              第i行第j列的元素
  
 
3)指向字符串的指针
  字符串的表示形式:
   a.用字符数组实现: static char string[]="I Love China!";
   b.用字符指针实现: char *p;
      char *s="I Love China!";

 例子:统计单词个数
#include<stdio.h>
#include<string.h>
main()
{
int stat(char *p);/*函数说明*/
char s[80];
int k;
printf("please input string:\n");
gets(s); /*输入一个字符串*/
k=stat(s); /*函数调用*/
printf("%d\n",k);
}
int stat(char *p) /*函数定义*/
{
int i,k,word;
for(i=0,word=0,k=0;*(p+i)!='\0';i++)
if(*(p+i)==' ') /*遇到了空格*/
word=0; /*处于非单词状态*/
else if(word==0) /*遇到了单词的第一个字母*/
{
k++;
word=1;
}
return(k);
}

   

范例:
1.输出变量的地址
int a=0;
printf("%x\n",&a);
2.指针变量的截断
main()
{
int a=0x12345678;
short *b=NULL;
b=&a;
printf("a=%x,*b=%x,&a=%x,b=%x",a,*b,&a,b);
}
输出结果:a=12345678,*b=5678,&a=13ff7c,b=13ff7c
a的值低位存在低地址,高位存在高地址,计算从低位开始读。

示意图:   &a的地址,连续占4个字节   13ff7c   78   存一个字节
                                     13ff7d   56   存一个字节
                                     13ff7e   34   存一个字节
                                     13ff7f   12   存一个字节
            从低位往高位读,b=&a,b为short型,它占两个字节,故取前两位

3.强制类型转换
*(int *)b 对short 型的b转换为int 型的b ,就可以得到正确结果

main()
{
int a=0x12345678;
short *b=NULL;
b=&a;
printf("a=%x,*b=%x,&a=%x,b=%x",a,*(int *)b,&a,b);
}
输出结果:a=12345678,*b=12345678,&a=13ff7c,b=13ff7c


4.指针自加1
 main()
{
int a =0x12345678;
 int *ap=NULL
  short *b=NULL;
  ap=&a;
  b=(short *)ap;
  b++;
  printf("a=%x,*ap=%x,*b=%x,&a=%x,ap=%x,b=%x\n",a,*ap,*b,&a,ap,b);
}
输出结果:a=12345678,*ap=12345678,*b=1234,&a=13ff7c,ap=13ff7c,b=13ff7e

5.void 指针和空指针
 short *b=NULL;
 #define NULL ((void *)0)
void 型指针可以指向任何变量,但是,void指针不能做增减运算。
main()
{
  void *a=NULL;
float b=0;
 a=&b;
*a=*a+1; //不能修改值,因为不知道类型,不能进行字节对应,编译通不过
}
 
空指针为赋值为NULL的指针,NLL的定义为#define NULL ((void *)0)
printf("NULL=%d\n",NULL);输出为0
6.使用指针引用二维数组
short array[2][2];
*(*(array+1)+1)

数组指针

类型说明符 (*指针变量名)[长度]
类型说明符表示该指针变量所指向数组的数据类型,“*”表示定义的变量为指针类型
“长度”表示二维数组的列长度,即二维数组的列数
(* 指针变量名)两边的括号不能少

#include<stdio.h>
   #define M 3
   #define N 4
   void main(void)
   {
   int i,j;
   short array[M][N]={0,1,2,3,4,5,6,7,8,9,10,11};
   short (*pst)[N]=NULL;
   pst=array;
  for(i=0;i<M;i++)
   {
    for(j=0;j<N;j++)
       printf("%4d ",*(*pst+j));
      pst++;
 
  }
  printf("\n");
 }

指针数组 int *parray[5];
指针数组可以指向二维数组


6.内存分配
 指针与内存分配
int *p=NULL;
p=(int *)malloc(100);为指针P分配了一个长度为100字节的内存区域
用free(p)释放。
需要包含#include<malloc.h>或#include<stdlib.h>
1 #include<stdio.h>
  2 #include<malloc.h>
  3 void main(void)
  4 {
  5  char *p=NULL;
  6  p=(char *)malloc(100);
  7 p=p+5;//错误语法,不能改变
  8 printf("please input your string:");
  9 scanf("%s",p); //遇到空格停止读入    
    gets(p);//按回车停止读入,在linux下会出现警告提示
 10 printf("your input is :%s\n",p);
   free(p);
       p=NULL;//记住这条,保护性赋值
 11 }
                

 


结构体和共用体

 结构:将不同数据类型、但相互关联的一组数据,组合成一个有机整体使用,C语言提供一种称为“结构”的数据结构。
 定义结构体:
 struct 结构体类型名
 {
 类型标识符  成员名1;
 类型标识符  成员2;
 };分号不能少
 struct student
 {
 ;
 定义结构体类型变量的定义
 形式:struct 结构体名 结构体变量名表
 eg:struct student stu1,stu2;
 或 struct student
 {
 }student1,student2;
 结构体可以嵌套。


共用体
   使几个不同的变量占用同一段的内存空间的结构称为共用型,一种特殊的结构体
   定义:
   union 共用类型名
   {成员列表;};
   共用变量的定义--与结构体变量的定义类似
   1)间接定义;先定义类型,再定义变量。
   union [data] {int i;          //data可以省略
                  char ch;
                  float f;}un1,un2,un3;
     共用体变量占用的内存空间等于最长成员的长度,而不是各个成员长度之和。
     特点
     1)系统采用覆盖技术,实现共用变量各成员内存共享,所以在某一时刻,存放的和起作用的是最后一次
      存入的成员值。
     2)由于所有成员共享一内存空间,故共用体变量与其各成员的地址相同。不能对共用变量进行初始化。


   枚举型
      枚举型的定义: enum 枚举类型名{取值表};
      eg:enum weekdays {sum,Mon,Tue,Wed,Thu,Fri,Sat};
      枚举变量的定义:与结构变量类似
      a.间接定义:enum weekdays workday;
      b.直接定义:enum [weekdays]{sum,Mon,Tue,...Sat}workday;
      说明:枚举仅仅适用于取值有限的数据。取值表中的值称为枚举元素,其含义由程序解释。
      枚举元素作为常量是有值的:定义时的顺序号(从0开始),所以枚举元素可以进行比较,比较规则是:
      序号大者为大。

   
 用type定义类型
    可以使用typedef定义已有类型的别名。该别名与标准类型名一样
    定义已有类型别名的方法如下:
    1)按定义变量的方法,写出定义体 float f;
    2)按变量名换成别名       float REAL;
    3)在定义体最前面加上typedef,   typedef float REAL;
     Example :给如下所示的结构体类型struct date定义一个别名DATE;
     struct date{int year,month,day;};
     在定义体最前面加上typedef:  如:typedef struct date{} DATE;
    typedef与#define 有相似之处,但是两者是不同的:前者是由编译器在编译时处理的,后者由编译预
    处理器在编译预处理时处理的,而且只能作简单的字符串替换

 位段结构
     有时候 存储1个信息不必占用一个字节,只需二进制的1(或多个)位就够。如果仍用结构体类型,则造成空间的浪

费。
     需要使用位段类型。
    1.位段的概念与定义
    是一种特殊的结构类型,其所有成员均以二进制位为单位定义长度,并称为成员位段。
    例如CPU的状态寄存器,按位段定义:
    struct status
    { 
     unsigned sign:  1;/*符号标志*/
     unisgned zero:  1;/*零标志*/
     unsigned carry:  1;
     unsigned parity: 1;
     unsigned :       0; /*长度为0的无名位段*/
     unsigned  parity: 1;
     unsigned  half_carry: 1;
     unsigned negative: 1;
    } flags;

    原来6个标志位是连续存储在一个字节中的。由于加入了一个长度为0的无名位段,所以其后的3个位段,从下
    一个字节开始存储,一共占用2个字节。
    2.一个位段必须存储在一个存储单元中(通常为1字节),不能夸两个。如果本单元不够容纳某位段,则从下一个单元

开始存储该位段。
    3.可以用%d,%x,%u,%o来格式字符,以整数形式输出位段。
    4.在数值表达式中引用位段时,系统自动将位段转换为整型数。

预处理命令
 1)执行宏定义(宏替换)
  定义: #define 宏名 串(宏体)
  如: #define PI 3.14159
  宏定义后,该程序中的宏名就代表了该字符串
  说明:#undef终止宏定义的作用域; #undef PI;
 宏定义的嵌套
   #define R 3.0
   #define PI 3.14159
   #define L 2*PI*R
带参数的宏定义:
   #define 宏名( 参数列表) 字符串
   #define S(a,b)  a*b
   宏体中的形参按从左到右的顺序被实参替换
   引用宏只占编译时间,不占运行时间
   #define squ(n) n*n
 main()
 {
  printf("%f\n",27.0/squ(3.0));
  }程序输出结果为:27.00000
类似于:27.0/3.0*3.0 仅仅进行字符串的替换

 2)包含文件#include
  一个源文件可以将另一个源文件的全部内容包含进来
  #include命令有两种格式:
   a.#include<文件名>
    包含标准头文件(/usr/include下的)
   b.#include "文件名"
    包含自定义的头文件(在当前目录)
 
 3)条件编译
   可以用于调试。
  a.控制条件为常量表达式的条件编译
   #if 常量表达式   为真的时候编译,否则程序段不编译
      程序段
   #endif
比写if条件进行判断要好,生成的二进制文件要小点
    #if  常量表达式
    程序段1
   #else
   程序段2
   #endif

 b.形式二
  #ifdef 标识符
   程序段
   #endif
当标识符在该条件编译结构前已定义时,程序被编译。
#ifdef 标识符
   程序段1
#else
  程序段2
   #endif
形式三:
   #ifndef
  程序段1
   #else
  程序段2
   #endif