函数
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//参数传值调用
在C语言中,被调函数不能直接修改主调函数中变量的值,而只能修改函数私有的临时副本的值
必要时,也能够修改主调函数中的变量。需要向被调用函数提供待设置值的变量的地址(指针)。被调用函数则需要将对应的参数声明为指针类型,并通过它间接访问变量。
如果是数组参数,当把数组名用作参数时,传递给函数的值是数组起始元素的位置或地址,并不复制数组元素的本身。在被调函数中,可以通过数组下标访问或修改数组元素的值。
————————————————————————————————————————————
变量
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
自动变量(局部变量) //在一个函数开头或段开头处说明的变量
作用域:
仅在定义它的函数内;
初始化:
不自动赋初值,使用前需要赋值;
值的保持:
随函数的引用而存在和消失,不保持值;
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
外部变量(全程变量) //在函数外部定义的变量
优势:解决函数单独编译的协调;与变量初始化有关;外部变量的值是永久的;解决数据共享;
作用域:整个程序;
初始化:0;
值的保持:永久保持
特点:
- c程序可以分别放在几个文件上,每个文件可以作为一个编译单位分别进行编译。外部变量只需在某个文件上定义一次,其它文件若要引用此变量时,应用extern加以说明(外部变量定义时不必加extern关键字)
- 在同一文件中,若前面的函数要引用后面定义的外部(在函数之外)变量时,在函数里加extern加以说明。
举例:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
静态变量 - 内部 //在局部变量前加上static
作用域:仅在定义它的函数内;
初始化:0;
值的保持:当函数执行完,返回调用点时,该变量并不撤销,再次调用时,其值将继续存在;
特点:采用静态存贮分配(由编译程序在编译时分配,而一般的自动变量和函数形参均采用动态存贮分配,即在运行时分配空间);
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
静态变量 - 外部 //在函数外部定义的变量前加static
优势:可以实现数据隐藏
作用域:定义它的文件(该文件的私有变量),其他文件上的函数不允许访问;
初始化:0;
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
寄存器变量 //只有自动(局部)变量和函数参数才能进一步指定为寄存器存贮类
特点:
- 使用register变量可以提高存取速度,但寄存器变量的数目依赖于具体机器,声明多了也只有前几个有效。
- 只限于int,char,short ,unsigned和指针类型用寄存类。
- 不能对register变量取地址(即&操作)
————————————————————————————————————————————
常量
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
enum 枚举常量
不同枚举中的名字必须互不相同,同一枚举中不同的名字可以是用相同的值;相对#define来说,优势在于常量值可以自动生成
举例:
-
enum week {MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY};
/*
week是新的数据类型,源于int,此时:
MONDAY = 0
TUESDAY = 1
WEDNESDAY = 2
THURSDAY = 3
FRIDAY = 4
SATURDAY = 5
SUNDAY = 6
*/
-
enum week {MONDAY = 1, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY};
/*
MONDAY = 1
TUESDAY = 2
WEDNESDAY = 3
THURSDAY = 4
FRIDAY = 5
SATURDAY = 6
SUNDAY = 7
*/
-
enum escapes {BELL = 'a', BACKSPACE = '', TAB = ' ', NEWLINE = 'n', VTAB = 'v', RETURN = ' '};
/*
BELL = 'a',
BACKSPACE = '',
TAB = ' ',
NEWLINE = 'n',
VTAB = 'v',
RETURN = ' '
*/
————————————————————————————————————————————
关键字
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const //限定符,通过 const 对变量进行限定后,无法修改其值,数组同理。
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
register //请求编译器尽可能的将变量存在CPU内部寄存器中,而不是通过内存寻址访问,以提高效率。
- register变量必须是能被CPU所接受的类型。这通常意味着register变量必须是一个单个的值,并且长度应该小于或者等于整型的长度。
- 不能通过 & 来取地址
————————————————————————————————————————————
数组
注意:数组名即数组的第一个元素的地址,所以使用scanf函数的时候可以不使用 &
// 调用数组a[i]的值时也可以写成 *(a+i) 的形式,编译的过程实际上先将其转换成 *(a+i) 再求值。所以 a[i] 与 *(a+i) 是等价的,一个通过数组和下表实现的表达式可以通过指针可偏移量实现。注意:指针是一个变量, p=a或p++是合法的,但数组名不是变量,不可以执行上面的操作。
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
字符数组 //scanf在读取字符串的时候不使用取址操作符 &,同指针
char str[1000];
scanf("%s",str);
————————————————————————————————————————————
指针
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
地址运算符 & 只能应用于内存中的对象(变量与数组元素)。它不能作用于表达式/常量/register类型的变量。
取值运算符 * ,在定义指针时表示定义的是个变量,在其他地方表示取指针变量的那个值
指针只能指向某种特定类型的对象(例外情况:指向void类型的指针可以存放指向任何类型的指针,但它不能间接引用其自身)。
定义:
int a;
int *p;
p = &a;
scanf("%d",p);
printf("%d ",*p);
//指向数组的普通指针(非数组指针)
#include <stdio.h>
int main(int argc, char const *argv[])
{
//char
char *p;//定义一个字符指针
char str[1000];//定义一个字符数组
scanf("%s", str);//字符数组输入
p = str; //p指针指向str首地址
printf("%c %c ", *p, *(p + 1)); //打印首地址的值与第二个地址的值
//int
int a[5] = {1, 2, 3, 4, 5};
int *q;
q = a;
//或写成 int *q = a;
printf("%d %d ", *q, *(q + 1));
return 0;
}
//指向指针的指针
定义:
#include <stdio.h>
int main(int argc, char const *argv[])
{
int num = 520;
int *p = #
int **pp = &p;//pp为指向指针的指针
return 0;
}
num |
520 |
|
|
//int num中存放的值是520 |
p |
&num |
|
|
//指针p中存放的是num的地址 |
*p |
num |
520 |
|
//对指针p解引用得到&num存放的值 == 520 |
pp |
&p |
|
|
//pp作为指向指针p的指针,存放的是p的地址 |
*pp |
p |
&num |
|
//pp解引用得到的是 p == &num |
**pp |
*p |
num |
520 |
//对pp二层解引用得到的是 *p == num ==520 |
举例:
#include <stdio.h>
int main(int argc, char const *argv[])
{
char *str[] = {"aaa", "bbb", "ccc", "ddd", "eee", "fff"};
//定义这个指针数组,此时指针数组存放的都是指针,字符串是指向字符的指针,数组每个元素都是指针,数组又可以用指针的方式来访问,所以我们可以用指向指针的指针来访问指针数组;
char **p;//定义指针p为指向指针的指针
char **str2[4];//str2数组中存放了4个指向指针的指针
p = &str[5];//p指向"fff",取址得到字符串"fff"的地址,此时p的一个指向字符指针的指针
str2[0] = &str[0];//str2数组中的指向指针的指针分别指向str数组中的指针
str2[1] = &str[1];
str2[2] = &str[2];
str2[3] = &str[3];
printf("%s ", *p);
for (int i = 0; i < 4; ++i)
{
printf("%s ", *str2[i]);
}
return 0;
}
//指向常量的指针 (指针本身可以被修改)
//const int *p可以指向const int 和int,但都不可以通过*p赋值
#include <stdio.h>
int main(int argc, char const *argv[])
{
int num = 520;
const int cnum = 820;
const int *p = &cnum;
printf("%d %d ", &cnum, cnum);
printf("%d %d ", p, *p);
p = #
//*p=1024; 此条无法通过编译,*p read-only
num = 1024;
return 0;
}
//常量指针 (指针本身不可以被修改)
//int * const p定义了指向之后,无法指向其他量
//指向非常量的常量指针:指向的值可以被修改
//指向常量的常量指针:指向的值不可以被修改
//cmd下无法被编译,unix和codeblocks可以编译
#include <stdio.h>
int main(int argc, char const *argv[])
{
int num = 520;
const int cnum = 820;
int *const p =&cnum;
*p = 1024;
printf("%d ", *p );
//p=&cnum; 常量指针指向num后,不可以修改指向
return 0;
}
//指向"指向常量的常量指针"的指针
#include <stdio.h>
int main(int argc, char const *argv[])
{
int num = 520;
const int cnum = 820;
const int *const p = &cnum;//定义一个指向常量的常量指针
const int *const *pp = &p;//定义一个指向"指向常量的常量指针"的指针
printf("%p %p ", pp, &p);//打印p的地址,pp和&p相同
printf("%p %p %p ", *pp, p, &cnum);//打印num的地址,*pp和p和&num相同
printf("%d %d %d ", **pp, *p, cnum);//打印三者的值,820
return 0;
}
//参数指针
语法:double atof(char *);
/* atof的参数时一个指向char类型的指针*/
//void指针
将任意类型的指针可以转换成为void指针;
但是void指针转化为其他类型需要使用强制转换;
#include <stdio.h>
int main(int argc, char const *argv[])
{
int num = 1234;
int *pi = #
char *ps = "abcd";
void *pv;
pv = pi; //pv变成指向整型的指针;
printf("%p %p ", pi, pv); //两个指针分别的地址;
//printf("%d ", *pv);//尝试解引用一个void指针,不知道void类型的宽度,编译器无法直接完成
printf("%d ", *(int *)pv); //将pv强制转换成为int类型的指针,并解引用
pv = ps;
printf("%p %p ", ps, pv);
//printf("%s ", pv);//字符串只需要指向首字符的地址就可以了,但是这样写是不规范的
printf("%s ",(char *)pv);
return 0;
}
//NULL指针 //空指针,#define NULL ((void *)0) 指针指向0
#include <stdio.h>
int main(int argc, char const *argv[])
{
int *p1; //p1成为迷途指针,悬空指针,即没有指定的指针
int *p2 = NULL;
printf("%p %p ", p1, p2);//p1地址随机,p2地址 00000000
printf("%d %d ", *p1, *p2); //unix下编译得到 *p1的值是随机的,*p2返回segmentation fault,因为对NULL指针解引用是非法的,程序崩溃报错
return 0;
}
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
指针运算
// *ip自增
语法:
- *ip += 1;
- ++*ip;
- (*ip)++;
//指针间赋值
语法: iq = ip;
/* 如果iq和ip同样是指向整型的指针,则将ip中的值copy到iq中,iq也将指向ip所指向的对象 */
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
指针传递
语法:
swap(a,b);
void swap(int *p,int *q)
特点:
- 调用方法传参,仅改变指针p的地址 &p ;
- *p=12赋值,仅改变内存块的值 *p ;
- p=&a指向变量a,仅改变内存块位置 p ;
- 调用方法结束,p的地址和内存块都不变,但如果在调用过程中内存块被修改,则值改变 ;
举例:
#include <stdio.h>
void pointer(int *p)
{
printf(" the p is %p , addr is %d, *p is %d", p , &p, *p);
//Line 2 指针p传入方法pointer中:在新的方法中生成了一个p的拷贝p1,新的地址 6356752,但值和指向的内存块数据没变
*p = 12;
printf(" the p is %p , addr is %d, *p is %d", p , &p, *p);
//Line 3 p1改变了其所指向的内存块的值为12,内存块 00F0FF2C 的值变成了22
int a = 11;
p = &a;
printf(" the p is %p , addr is %d, *p is %d", p , &p, *p);
//Line 4 p1的值指向a,即p1指向a 内存块 0060FEFC,此时p1与p分别指向不同的内存块了,不会互相影响
}
int main()
{
int b = 22;
int *p = &b;
printf(" the p is %p , addr is %d, *p is %d", p , &p, *p);
//Line 1 *p指向b的地址:获得了p(内存块数据)&p(地址)*p(值)
pointer(p);
printf(" the p is %p , addr is %d, *p is %d", p , &p, *p);
//Line 5 方法结束,调用方法结束后,p地址仍是调用前的地址,地址和值没变(改变的仅仅是p的拷贝p1),但是p所指向的内存块数据被p1所改变了,故*p为12
}
输出:
其他传递方式(来源参考他人)
-
传值方式 //和函数的值传递不是一个概念
语法:
swap(&a , &b);
void swap(int *a , int *b)
// 传入的是变量a的地址 &a,函数接收到的是传入地址的值
// 使用指针方式修改指向内存块的值,传入的是 a、b变量地址的拷贝。
注意:函数的值传递的方式是
swap(a , b);
void swap(int a , int b)
-
传引用方式
语法:
swap(a,b);
void swap(int &p,int &q)
// 使用引用方式,传入的是变量a、b,而不是拷贝,地址不变。
举例:
#include <stdio.h>
void swap(int *a , int *b)
//方法一:传值方式
{
printf(" /*** Method 1 ***/");
printf(" &a addr : %d , &b addr: %d", &a, &b);
printf(" a memory : %d , b memory: %d", a, b);
printf(" *a : %d , *b : %d", *a, *b);
int temp = *a;
*a = *b;
*b = temp;
}
void swap(int &a , int &b)
//方法二:传引用方式
{
printf(" /*** Method 2 ***/");
printf(" &a addr : %d , &b addr: %d", &a , &b);
int temp = a;
a = b;
b = temp;
}
int main(int argc, char const *argv[])
{
int a = 3 , b = 5;
printf(" &a addr : %d , &b addr: %d", &a , &b);
printf(" a : %d , b : %d", a , b);
swap(&a , &b); //方法一
printf(" &a addr : %d , &b addr: %d", &a, &b);
printf(" a : %d , b : %d", a , b);
swap(a , b); //方法二
printf(" a : %d , b : %d ", a , b);
return 0;
}
输出:
————————————————————————————————————————————
数组指针和指针数组
注意:赋值符号"="号两边的数据类型必须是相同的,通过这点可以排除在写数组与指针时的错误;
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
数组指针 //指向数组的指针
注意:不同的数据类型占的地址数(字节)不同,char:1,int:4,float:4,double:8
定义:
//一维
#include <stdio.h>
int main(int argc, char const *argv[])
{
//int,char同理
int temp[5] = {1, 2, 3, 4, 5};
int (*p)[5] = &temp;//指针p指向temp数组的首地址
int i;
for (i = 0; i < 5; ++i)
{
printf("%d ", *(*p + i));
//*p指向temp数组的首地址,*p+i代表后移几个字节的地址,*(*p+i)代表指向地址的值
}
return 0;
}
//二维,行指针
#include <stdio.h>
int main(int argc, char const *argv[])
{
int array[4][5] = {{1, 2, 3, 4, 5}, {11, 12, 13, 14, 15}, {21, 22, 23, 24, 25}, {31, 32, 33, 34, 35}, {41, 42, 43, 44, 45}};
int (*p)[5];//该语句是定义一个数组指针,指向含4个元素的一维数组。或直接写成 int (*p)[5] = array;
p = array;//将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
//在这里,数组指针p共有三个int指针,赋值时意味着将p的三个指针指向 array第一行的三个元素;
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 5; ++j)
{
printf("%2d ", *(*(p + i) + j));
}
printf(" ");
}
return 0;
}
另一种表达方式:
...
int (*p)[4][5]=&array;
...
printf("%2d ", *(*(*p + i) + j));
...
//都表示数组中i行j列一个元素
*(p+i) == p[i]
*(*(p+i)+j) == p[i][j] == *(p[i]+j) == (*(p+i))[j]
*(*(*(p+i)+j)) == p[i][j][k]
p++;//该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]
大小:在32位系统下永远是4字节
例1:
#include <stdio.h>
int main(int argc, char const *argv[])
{
int a[10];
int *p, i;
for (i = 0; i < 10; ++i)
{
scanf("%d", &a[i]);
}
for (p = &a[0]; p < (a + 10); p++)
//p+i = a+i = &a[i] 这三者意思相同,所以循环结构使用 p < (a + 10),等价于 i < 10
{
printf("%d ", *p);
}
return 0;
}
例2:
#include <stdio.h>
int main()
{
//数组指针的大小必须与数组相同,否则编译不通过,此处均为[5]
char a[5] = {'a', 'b', 'c', 'd'};
//p1 和p2 都是数组指针,指向的是整个数组
//&a 是整个数组的首地址,a是数组首元素的首地址
char (*p1)[5] = &a;
//p1指向&a是对的,左右都是整个数组的首地址
char (*p2)[5] = a;
//p2指向a,编译时会出现warming,但是"="两边的数据类型不一致,左边的是指向整个数组的指针,右边的数据类型是指向单个字符的指针。
char (*p3)[5] = (char (*)[5])a;
//p2类型如果要正常使用的话则使用p3这种强制转换,右边强制转换成为a整个数组的首地址
printf("a=%d ", a);
printf("a=%c ", a[0]);
printf("&a=%d ", &a);
printf("p1=%c ", **p1);
printf("p2=%c ", **p2);
printf("p3=%c ", **p3);
printf("p1+1=%c ", **(p1 + 1));
printf("p2+1=%c ", **(p2 + 1));
printf("p3+1=%c ", **(p3 + 1));
return 0;
}
输出:
出现的warming信息为p2导致,左右类型不一致
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
指针数组 //用于存储指针的数组
定义:
//一维
#include <stdio.h>
int main(int argc, char const *argv[])
{
//int
int a = 1;
int b = 2;
int c = 3;
int d = 4;
int e = 5;
int *p[5] = {&a, &b, &c, &d, &e};//数组里面每一个元素都指向abcde的地址
for (int i = 0; i < 5; ++i)
{
printf("%d ", *p[i]);//循环打印每一个指针对应地址的值 *
}
//char
char *q[5] = {"aaa", "bbb", "ccc", "ddd", "eee"};
for (int j = 0; j < 5; ++j)
{
printf("%c ", *q[j]); //加*表示取出的是字符
}
for (int k = 0; k < 5; ++k)
{
printf(" %s", q[k]);//不加*表示字符串
}
return 0;
}
//二维
int *p[3]; //定义的p数组中的三个元素都是int指针,分别是p[0]、p[1]、p[2],所以要分别赋值。
int a[3][4];
p++; //该语句表示p数组指向下一个数组元素。注:此数组每一个元素都是一个指针
for(i=0;i<3;i++)
p[i]=a[i];
//都表示数组中i行j列一个元素
*(p+i) == p[i]
*(*(p+i)+j) == p[i][j] == *(p[i]+j) == (*(p+i))[j]
*(*(*(p+i)+j)) == p[i][j][k]
赋值:*p=a; //这里*p表示指针数组第一个元素的值,a的首地址的值。
大小:数组的大小由数组本身来决定,32位系统下每个指针占4字节,大小不确定;
举例:
#include <stdio.h>
int main()
{
int i;
char c1[] = "How";
char c2[] = "are";
char *c3 = "you"; //该指针变量指向字符串所在字符数组的首地址。
char *pArray[3]; //该数组元素可以指向char类型或数组类型
pArray[0] = c1; //指针变量pArray[0]指向了c1的首地址
pArray[1] = c2;
pArray[2] = c3;
for(i = 0; i < 3; i++)
printf("%s ", pArray[i]); //printf("%s ", pArray[0])等价于printf("%s ", c1)
return 0;
}
输出:
出处: http://www.cnblogs.com/hongcha717/archive/2010/10/24/1859780.html
出处: http://www.cnblogs.com/mq0036/p/3382732.html
————————————————————————————————————————————
结构体
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
编译器会将结构体的数据进行内存对其,如果定义 char a;int b;char c;
对其前:6个字节
a |
b |
b |
b |
b |
c |
对其后:12个字节
a |
|
|
|
b |
b |
b |
b |
c |
|
|
|
如果定义的是 char a;char c;int b;
则调整为
a |
c |
|
|
b |
b |
b |
b |
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
定义
#include <stdio.h>
//第一种定义方法
struct Book //定义结构体名
{
char title[128];
char author[40];
float price;
unsigned int date;//无符号整型
char publisher[40];
} book;//book是结构体变量名
//第二种定义方法
struct Book2
{
float price2;
unsigned int date2;
} book2;
//第三种定义方法
struct Book3
{
int number;
int number2;
} book3 = {1, 2};
//结构体嵌套
struct Data
{
int year;
int month;
int day;
};
struct Nested
{
struct Data data;
char a;
} nested =
{
{2017, 4, 17},
'i'
};
int main(void)
{
//第一种初始化方法
struct Book book =//初始化结构体
{
"aaa",
"bbb",
12.0,
20170417,
"ccc"
};
//第二种初始化方法
struct Book2 book2 =//通过这种写法只初始化某一个变量
{
.price2 = 48.0
};
//scanf("%s",book.title);
//scanf("%s",book.author);
//...
printf("%s ", book.title);
printf("%s ", book.author);
printf("%f ", book.price);
printf("%d ", book.date);
printf("%s ", book.publisher);
printf("- - - - - - - - ");
printf("%f ", book2.price2);
//...
printf("- - - - - - - - ");
//结构体嵌套打印
printf("%d %d %d ", nested.data.year, nested.data.month, nested.data.day);
return 0;
}
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
结构体数组
#include <stdio.h>
//第一种定义方法
struct Way1
{
char a;
char b;
int c;
} way1 [3];
//第二种定义方法
struct Way2
{
char d;
char e;
int f;
};
struct Way2 way2 [10];
int main(int argc, char const *argv[])
{
//初始化结构体数组
struct Way1 way1[3] =
{
{'a', 'b', 0},
{'c', 'd', 1},
{'e', 'f', 2}
}
return 0;
}
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
结构体指针
#include <stdio.h>
struct Book
{
char title[128];
char author[40];
float price;
unsigned int date;
char publisher[40];
} book =
{
"aaa",
"bbb",
12.0,
20170417,
"ccc"
};
int main(void)
{
struct Book *pt;
pt = & book;//结构体的变量名并不是指向结构体的地址,定义结构体指针时必须使用 &
//第一种方法
printf("%s ", (*pt).title);
printf("%s ", (*pt).author);
printf("%f ", (*pt).price);
printf("%d ", (*pt).date);
printf("%s ", (*pt).publisher);
printf("- - - - - - - - - - - ");
//第二种方法
printf("%s ", pt->title);
printf("%s ", pt->author);
printf("%f ", pt->price);
printf("%d ", pt->date);
printf("%s ", pt->publisher);
return 0;
}
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
传递结构体变量
#include <stdio.h>
struct Date
{
int year;
int month;
int day;
};
struct Book
{
char title[128];
float price;
struct Date date;
};
struct Book getInput(struct Book book);
struct Book getInput(struct Book book)
{
printf("input the title:");
scanf("%s", book.title);
printf("input the price:");
scanf("%f", &book.price);
printf("input the date:");
scanf("%d %d %d", &book.date.year, &book.date.month, &book.date.day);
return book;
}
struct Book printBook(struct Book book);
struct Book printBook(struct Book book)
{
printf("%s ", book.title);
printf("%f ", book.price);
printf("%d-%d-%d", book.date.year, book.date.month, book.date.day);
}
int main()
{
struct Book b1, b2;
printf("input the first info ");
b1 = getInput(b1);
printBook(b1);
return 0;
}
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
传递指向结构体变量的指针 //基本结构同上
#include <stdio.h>
struct Date
{
int year;
int month;
int day;
};
struct Book
{
char title[128];
float price;
struct Date date;
};
void getInput(struct Book *book);
void getInput(struct Book *book)//不需要返回值,直接定义成为void即可
{
printf("input the title:");
scanf("%s", book->title);
printf("input the price:");
scanf("%f", &book->price);
printf("input the date:");
scanf("%d %d %d", &book->date.year, &book->date.month, &book->date.day);
}
void printBook(struct Book *book);
void printBook(struct Book *book)
{
printf("%s ", book->title);
printf("%f ", book->price);
printf("%d-%d-%d", book->date.year, book->date.month, book->date.day);
//void类型没有返回值 return
}
int main(void)
{
struct Book b1, b2;
printf("input the first info ");
getInput(&b1);//调用时直接取地址
printBook(&b1);
return 0;
}
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
动态申请结构体
//使用malloc函数为结构体分配存储空间,free函数释放
#include <stdio.h>
#include <stdlib.h>
struct Date
{
int year;
int month;
int day;
};
struct Book
{
char title[128];
float price;
struct Date date;
};
void getInput(struct Book *book);
void getInput(struct Book *book)//不需要返回值,直接定义成为void即可
{
printf("input the title:");
scanf("%s", book->title);
printf("input the price:");
scanf("%f", &book->price);
printf("input the date:");
scanf("%d %d %d", &book->date.year, &book->date.month, &book->date.day);
}
void printBook(struct Book *book);
void printBook(struct Book *book)
{
printf("%s ", book->title);
printf("%f ", book->price);
printf("%d-%d-%d", book->date.year, book->date.month, book->date.day);
}
int main(void)
{
struct Book *b1, *b2;
b1 = (struct Book *)malloc(sizeof(struct Book));
if (b1 == NULL)
{
printf("内存分配失败 ");
exit(1);
}
printf("input the first info ");
getInput(b1);//因为b1是指针,所以不需要取地址操作符
printBook(b1);
free(b1);
return 0;
}
————————————————————————————————————————————
预处理器
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
文件包含 #include "文件名" 或 #include <文件名>
- 用" "引起来,则在源文件所在的位置查找该文件;
- 如果在该位置没有找到文件或用< >括起来的,则根据相应的规则查找该文件,这个规则同具体的实现有关
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
两个预处理语句 #ifdef 和 #ifndef
用法:
#ifndef HDR
#define HDR
/* hdr.h文件的内容在这里*/
#endif
/* 用来测试某个名字是否已经定义,等价于 */
#if !defined(HDR)
#define HDR
#endif
举例:
/* 测试SYSTEM变量后确定包含某个文件 */
#if SYSTEM == SYSV
#define HDR "sysv.h"
#elif SYSTEM == BSD
#define HDR "bsd.h"
#elif SYSTEM == MSDOS
#define HDR "msdos.h"
#else
#define HDR "default.h"
#endif
#include HDR
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
宏替换
-
#define 名字替换文本
用法:较长的替换文本可以在待续的行末尾加上反斜杠符
举例:
-
例:#define forever for(;;)
/* 该句为无限循环定义了一个新名字forever */
-
例:#define max(A,B) ((A)>(B)?(A):(B))
/* 形参A和B每次出现都将被替换成对应的实际参数,不过要适当使用括号来规范计算次序的正确性 */
-
例:#define dprint(expr) printf(#expr " = %g ",expr)
/* 当使用时dprintf(x/y);时被扩展为 printf("x/y" "= %g ",x/y); */
-
例:#define paste(front,back) front ## back
/*预处理器运算符## 为宏定义提供了连接实际参数的手段*/
/* 在调用paste(name,1)时,将建立记好 name1*/
-
-
#undef getchar
int getchar(void){...}
用法:通过#undef 取消名字的宏定义,这样可以保证后续的调用是函数调用,而不是宏调用
————————————————————————————————————————————
getchar /putchar 输入输出
语法:
c = getchar(); // 获取控制台输入
putchar(c); //输出
————————————————————————————————————————————
EOF,End Of File,文件尾标志。 从数值上来看,就是整数-1
linux:在输入回车换行后的空行位置,按 ctrl+d (先按ctrl键,不放,再按d键)
windows:在输入回车换行后的空行位置,按 ctrl+z,再回车确认
————————————————————————————————————————————