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