目录
1-11 作用域规则
任何一种编程中,作用域是程序中定义的变量所存在的区域,超过该区域变量就不能被访问。
C 语言中有三个地方可以声明变量:
- 在函数或块内部的局部变量
- 在所有函数外部的全局变量
- 在形式参数的函数参数定义中
1-11-1 局部变量
在某个函数或代码块的内部声明的变量称为局部变量。局部变量只能被函数或该代码块内部的语句使用。
下例中,a、b、c是main()函数的局部变量:
#include <stdio.h>
int main ()
{
/* 局部变量声明 */
int a, b;
int c;
/* 实际初始化 */
a = 10;
b = 20;
c = a + b;
printf ("value of a = %d, b = %d and c = %d\n", a, b, c);
return 0;
}
1-11-2 全局变量
全局变量是定义在函数外部,通常放在程序的顶部。全局变量可以被任何函数访问。也就是说,全局变量在声明后整个程序中都是可用的。
实例:
#include <stdio.h>
/* 全局变量声明 */
int g;
int main ()
{
/* 局部变量声明 */
int a, b;
/* 实际初始化 */
a = 10;
b = 20;
g = a + b;
printf ("value of a = %d, b = %d and g = %d\n", a, b, g);
return 0;
}
在程序中,局部变量和全局变量的名称可以相同,但是在函数内,如果两个名字相同,会使用局部变量值,全局变量不会被使用。实例如下:
#include <stdio.h>
/* 全局变量声明 */
int g = 20;
int main ()
{
/* 局部变量声明 */
int g = 10;
printf ("value of g = %d\n", g);
return 0;
}
以上代码编译后结果:
value of g = 10
1-11-3 形式参数
函数的参数,形式参数,被当作该函数内的局部变量,如果与全局变量同名它们会优先使用。
#include <stdio.h>
/* 全局变量声明 */
int a = 20;
int main ()
{
/* 在主函数中的局部变量声明 */
int a = 10;
int b = 20;
int c = 0;
int sum(int, int);
printf ("value of a in main() = %d\n", a);
c = sum( a, b);
printf ("value of c in main() = %d\n", c);
return 0;
}
/* 添加两个整数的函数 */
int sum(int a, int b)
{
printf ("value of a in sum() = %d\n", a);
printf ("value of b in sum() = %d\n", b);
return a + b;
}
以上代码编译后的结果:
value of a in main() = 10
value of a in sum() = 10
value of b in sum() = 20
value of c in main() = 30
全局变量与局部变量在内存中 的区别:
- 全局变量保存在内存的全局存储区中,占用静态的存储单元;
- 局部变量保存在栈中,只有在所在函数被调用时,才动态地为变量分配存储单元。
1-11-4 初始化局部变量和全局变量
当局部变量被定义时,系统不会对其初始化,必须要对其初始化。定义全局变量时,系统会自动对其初始化。初始化默认值如下所示:
数据类型 | 初始化默认值 |
---|---|
int | 0 |
char | '\0' |
float | 0 |
double | 0 |
pointer | NULL |
1-12 数组
C 语言支持数组数据结构,它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据,但它往往被认为是一系列相同类型的变量。
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99。而是声明一个数组变量,比如 numbers,然后使用 numbers[0]、numbers[1]、...、numbers[99] 来代表一个个单独的变量。数组中的特定元素可以通过索引访问。
所有的数组都是由连续的内存位置组成。最低的地址对应第一个元素,最高的地址对应最后一个元素。
1-12-1 声明数组
在 C 中要声明一个数组,需要指定元素的类型和元素的数量。一维数组的格式如下:
arraySize 必须是一个大于零的整数常量,type 可以是任意有效的 C 数据类型。
type arrayName [ arraySize ];
例如,声明一个类型为double的、包含10个元素的数组balance,其声明语句如下:
double balance[10];
现在 balance 是一个可用的数组,可以容纳 10 个类型为 double 的数字。
1-12-2 初始化数组
在C中,可以逐个初始化数组,也可以使用一个初始化语句,如下所示:
double balance[5] = {1000.0, 2.0, 3.4, 7.0, 50.0};
如果您省略掉了数组的大小,数组的大小则是初始化时元素的个数:
double balance[] = {1000.0, 2.0, 3.4, 7.0, 50.0};
当想要给一个数组当中某个元素赋值时,以下为“把数组中第五个元素的值赋为 50.0”的实例:
balance[4] = 50.0;
所有的数组都是以 0 作为它们第一个元素的索引,也被称为基索引,数组的最后一个索引是数组的总大小减去 1。例:第五个元素索引为4,所以第五个元素是balance[4],而不是balance[5]。
1-12-3 访问数组元素
数组元素可以通过数组名称加索引进行访问。元素的索引是放在方括号内,跟在数组名称的后边。
例如,把数组中第10个元素的值赋给salary变量:
double salary = balance[9];
实例:
以下实例使用了上述的三个概念,即,声明数组、数组赋值、访问数组。
#include <stdio.h>
int main ()
{
int n[ 10 ]; /* n 是一个包含 10 个整数的数组 */
int i,j;
/* 初始化数组元素 */
for ( i = 0; i < 10; i++ )
{
n[ i ] = i + 100; /* 设置元素 i 为 i + 100 */
}
/* 输出数组中每个元素的值 */
for (j = 0; j < 10; j++ )
{
printf("Element[%d] = %d\n", j, n[j] );
}
return 0;
}
以上代码编译后结果为:
Element[0] = 100
Element[1] = 101
Element[2] = 102
Element[3] = 103
Element[4] = 104
Element[5] = 105
Element[6] = 106
Element[7] = 107
Element[8] = 108
Element[9] = 109
1-12-4 数组详解
概念 | 描述 |
---|---|
多维数组 | C 支持多维数组。多维数组最简单的形式是二维数组。 |
传递数组给函数 | 可以通过指定不带索引的数组名称来给函数传递一个指向数组的指针。 |
从函数返回数组 | C 允许从函数返回数组。 |
指向数组的指针 | 您以通过指定不带索引的数组名称来生成一个指向数组中第一个元素的指针。 |
1-13 enum(枚举)
枚举是c语言中的一种基本数据类型,它可以让数据更简洁、更易读。
枚举语法定义格式为:
enum 枚举名 {枚举元素1,枚举元素2,……};
实例:
一星期有 7 天。如果不用枚举,我们先使用 #define 来为每个整数定义一个别名,则会代码有点多:
#define MON 1
#define TUE 2
#define WED 3
#define THU 4
#define FRI 5
#define SAT 6
#define SUN 7
接下来改用枚举的方式:
我们在这个实例中把第一个枚举成员的值定义为 1,第二个就为 2,以此类推。
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
注:若第一个枚举成员没指定值时,默认值为整型的 0,后续枚举成员的值在前一个成员上加 1。
也可以在定义枚举类型时改变枚举元素的值:
enum season {spring, summer=3, autumn, winter};
没有指定值的枚举元素,其值为前一元素加 1。也就说 spring 的值为 0,summer 的值为 3,autumn 的值为 4,winter 的值为 5。
1-13-1 枚举变量的定义
前面我们只是声明了枚举类型,接下来我们看看如何定义枚举变量。
我们可以通过以下三种方式来定义枚举变量:
1、先定义枚举类型,再定义枚举变量
enum DAY //DAY是枚举名称
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
enum DAY day; //day是枚举变量名
2、定义枚举类型的同时定义枚举变量
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;
3、省略枚举名称,直接定义枚举变量
enum
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;
实例:
#include<stdio.h>
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
int main()
{
enum DAY day; //定义枚举变量
day = WED; //将第三个枚举成员WED的值赋给了枚举变量day
printf("%d",day);
return 0;
}
以上实例结果为:
3
在C 语言中,枚举类型是被当做 int 或者 unsigned int 类型来处理的,所以按照 C 语言规范是没有办法遍历枚举类型的。
不过在一些特殊的情况下,“枚举类型必须连续”可以实现“有条件的遍历”。
以下实例使用 for 来遍历枚举的元素:
#include<stdio.h>
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;
int main()
{
// 遍历枚举元素
for (day = MON; day <= SUN; day++) {
printf("枚举元素:%d \n", day);
}
}
以上实例输出结果为:
枚举元素:1
枚举元素:2
枚举元素:3
枚举元素:4
枚举元素:5
枚举元素:6
枚举元素:7
以下例子中枚举类型不连续,这种枚举无法遍历:
enum
{
ENUM_0,
ENUM_10 = 10,
ENUM_11
};
枚举在 switch 中使用的实例:
#include <stdio.h>
#include <stdlib.h>
int main()
{
enum color { red=1, green, blue };
enum color favorite_color;
/* ask user to choose color */
printf("请输入你喜欢的颜色前面的序号: (1. red, 2. green, 3. blue): ");
scanf("%d", &favorite_color);
//scanf()不要忘记打&(地址)字符。
/* 输出结果 */
switch (favorite_color)
{
case red: //red=1,即case 1
printf("你喜欢的颜色是红色");
break;
case green: //将green换成2也是一样的
printf("你喜欢的颜色是绿色");
break;
case blue:
printf("你喜欢的颜色是蓝色");
break;
default:
printf("你没有选择你喜欢的颜色");
}
return 0;
}
输出结果为:
请输入你喜欢的颜色前面的序号: (1. red, 2. green, 3. blue): 1
你喜欢的颜色是红色
1-13-2 将整数转换为枚举
实例:
#include <stdio.h>
#include <stdlib.h>
int main()
{
//①先写一个枚举出来
enum day //enum类型名称为day
{
saturday,
sunday,
monday,
tuesday,
wednesday,
thursday,
friday
} workday; //此时枚举变量名为workday,在下面程序里没啥用
int a = 1;
enum day weekend; //②重新定义枚举变量名为weekend
weekend = ( enum day ) a; //③类型转换:将整数a=1由int型(用enum day)转换成枚举类型
//weekend = a; //错误
printf("weekend:%d",weekend);
return 0;
}
输出结果为:
weekend:1
1-14 指针
每一个变量都有一个内存位置,每一个内存位置都定义了可使用连字号(&)运算符访问的地址,它表示了在内存中的一个地址。
请看下面的实例,它将输出定义的变量地址:
#include <stdio.h>
int main ()
{
int var1;
char var2[10];
printf("var1 变量的地址: %p\n", &var1 );
printf("var2 变量的地址: %p\n", &var2 );
//%p是占位符,表示输出一个变量对应的内存地址编号(无符号十六进制整型数)
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
var1 变量的地址: 0x7fff5cc109d4
var2 变量的地址: 0x7fff5cc109de
1-14-1 什么是指针?
指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。指针储存地址,对应指针的值的类型是地址的长十六进制数。
就像其他变量或常量一样,您必须在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:
type *var-name; //var-name 是指针变量的名称
在这里,type 是指针的基类型,它必须是一个有效的 C 数据类型,var-name 是指针变量的名称。用来声明指针的星号 * 与乘法中使用的星号是相同的。但是,在这个语句中,星号*是用来指定一个变量是指针。以下是有效的指针声明:
int *ip; /* 一个整型的指针 */
double *dp; /* 一个 double 型的指针 */
float *fp; /* 一个浮点型的指针 */
char *ch; /* 一个字符型的指针 */
所有实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,对应指针的值的类型都是一样的,都是一个代表内存地址的长的十六进制数。
不同数据类型的指针之间唯一的不同是,指针所指向的变量或常量的数据类型不同。
1-14-2 如何使用指针?
①定义一个指针变量;②把变量地址赋值给指针;③访问指针变量中可用地址的值。
这些是通过使用一元运算符 ***** 来返回位于操作数所指定地址的变量的值。下面的实例涉及到了这些操作:
*ip、var都是int型,ip、&var都是十六进制整数型。
#include <stdio.h>
int main ()
{
int var = 20; /* 实际变量的声明 */
int *ip; /* 指针变量的声明 */
ip = &var; /* 在指针变量中存储 var 的地址 */
// *ip、var都是int型,ip、&var都是十六进制整数型
printf("Address of var variable: %p\n", &var );
/* 在指针变量中存储的地址 */
printf("Address stored in ip variable: %p\n", ip );
/* 使用指针访问值 */
printf("Value of *ip variable: %d\n", *ip );
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
Address of var variable: bffd8b3c
Address stored in ip variable: bffd8b3c
Value of *ip variable: 20
1-14-3 NULL指针
在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为空指针。
NULL 指针是一个定义在标准库中的值为零的常量。
#include <stdio.h>
int main ()
{
int *ptr = NULL;
printf("ptr 的地址是 %p\n", ptr );
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
ptr 的地址是 0x0
在大多数的操作系统上,程序不允许访问地址为 0 的内存,因为该内存是操作系统保留的。然而,内存地址 0 有特别重要的意义,它表明该指针不指向一个可访问的内存位置。但按照惯例,如果指针包含空值(零值),则假定它不指向任何东西。
如需检查一个空指针,可以使用 if 语句,如下所示:
if(ptr) /* 如果 p 非空,则完成 */
if(!ptr) /* 如果 p 为空,则完成 */
1-14-4 指针详解
概念 | 描述 |
---|---|
指针的算术运算 | 可以对指针进行四种算术运算:++、--、+、- |
指针数组 | 可以定义用来存储指针的数组。 |
指向指针的指针 | C 允许指向指针的指针。 |
传递指针给函数 | 通过引用或地址传递参数,使传递的参数在调用函数中被改变。 |
从函数返回指针 | C 允许函数返回指针到局部变量、静态变量和动态内存分配。 |