static 存储类指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。因此,使用 static 修饰局部变量可以在函数调用之间保持局部变量的值。
static 修饰符也可以应用于全局变量。当 static 修饰全局变量时,会使变量的作用域限制在声明它的文件内。
全局声明的一个 static 变量或方法可以被任何函数或方法调用,只要这些方法出现在跟 static 变量或方法同一个文件中。
#include
/* 函数声明 */
void func1(void);
static int count=10; /* 全局变量 - static 是默认的 */
int main()
{
while (count--) {
func1();
}
return 0;
}
void func1(void)
{
/* t 是 'func1' 的局部变量 - 只初始化一次
* 每次调用函数 'func1' 'thingy' 值不会被重置。
*/
static int t=5;
t++;
printf(" t 为 %d , count 为 %d\n", t, count);
}
实例中 count 作为全局变量可以在函数内使用,thingy 使用 static 修饰后,不会在每次调用时重置。
t 为 6 , count 为 9
t 为 7 , count 为 8
t 为 8 , count 为 7
t 为 9 , count 为 6
t 为 10 , count 为 5
t 为 11 , count 为 4
t 为 12 , count 为 3
t 为 13 , count 为 2
t 为 14 , count 为 1
t 为 15 , count 为 0
将static删除后
#include
/* 函数声明 */
void func1(void);
int count = 10; /* 全局变量 - static 是默认的 */
int main()
{
while (count--) {
func1();
}
return 0;
}
void func1(void)
{
/* 'thingy' 是 'func1' 的局部变量 - 只初始化一次
* 每次调用函数 'func1' 'thingy' 值不会被重置。
*/
int thingy = 5;
thingy++;
printf(" thingy 为 %d , count 为 %d\n", thingy, count);
}
thingy 为 6 , count 为 9
thingy 为 6 , count 为 8
thingy 为 6 , count 为 7
thingy 为 6 , count 为 6
thingy 为 6 , count 为 5
thingy 为 6 , count 为 4
thingy 为 6 , count 为 3
thingy 为 6 , count 为 2
thingy 为 6 , count 为 1
thingy 为 6 , count 为 0
p | q | p & q | p | q | p ^ q |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
0 | 1 | 0 | 1 | 1 |
1 | 1 | 1 | 1 | 0 |
1 | 0 | 0 | 1 | 1 |
假设如果 A = 60,且 B = 13,现在以二进制格式表示,它们如下所示:
A = 0011 1100
B = 0000 1101
-----------------
A&B = 0000 1100
A|B = 0011 1101
A^B = 0011 0001
~A = 1100 0011
1. 值传递
#include
void swap(int x, int y);
void swap(int x, int y)
{
int temp;
temp = x;
x = y;
y = temp;
}
int main( int argc, char *argv[] )
{
int a = 5;
int b = 10;
swap(a, b); //调用交换函数
printf("交换结果为 a = %d, b = %d\n",a,b);
return 0;
}
由于值传递是单向传递,传递过程中只是改变了形参的数值,并未改变实参的数值,因此并不会改变a和b原有的值。
2. 指针传递
#include
void swap(int *x, int *y);
void swap(int *x, int *y)
{
int temp;
temp = *x;
*x = *y;
*y = temp;
}
int main( int argc, char *argv[] )
{
int a = 5;
int b = 10;
swap(&a, &b); //调用交换函数
printf("交换结果为 a = %d, b = %d\n",a,b);
return 0;
}
指针传递过程中,将a和b的地址分别传递给了x和y,在函数体内部改变了a、b所在地址的值,即交换了a、b的数值。
3. 引用传递
#include
void swap(int &x, int &y);
void swap(int &x, int &y)
{
int temp;
temp = x;
x = y;
y = temp;
}
int main( int argc, char *argv[] )
{
int a = 5;
int b = 10;
swap(a, b); //调用交换函数
printf("交换结果为 a = %d, b = %d\n",a,b);
return 0;
}
引用传递中,在调用swap(a, b);时函数会用a、b分别代替x、y,即x、y分别引用了a、b变量,这样函数体中实际参与运算的其实就是实参a、b本身,因此也能达到交换数值的目的。
如果您想要在函数中传递一个一维数组作为参数,您必须以下面三种方式来声明函数形式参数,这三种声明方式的结果是一样的,因为每种方式都会告诉编译器将要接收一个整型指针。同样地,您也可以传递一个多维数组作为形式参数。
形式参数是一个指针
void myFunction(int *param)
{
.
.
.
}
形式参数是一个已定义大小的数组:
void myFunction(int param[10])
{
.
.
.
}
形式参数是一个未定义大小的数组:
void myFunction(int param[])
{
.
.
.
}
实例:
#include
using namespace std;
int sum(int a[], int size) {
int sum=0;
for (int i = 0; i < size; i++)
{
sum = sum + a[i];
}
return sum;
}
int main()
{
int a[5];
for (int i = 0; i < 5; i++)
{
cin >> a[i];
}
cout << sum(a, 5);
}
C 语言不允许返回一个完整的数组作为函数的参数。但是,您可以通过指定不带索引的数组名来返回一个指向数组的指针。
如果您想要从函数返回一个一维数组,您必须声明一个返回指针的函数
int * myFunction()
{
.
.
.
}
另外,C 不支持在函数外返回局部变量的地址,除非定义局部变量为 static 变量。
PS:随机数生成
#include
#include using namespace std; int main() { srand((unsigned)time(NULL)); int p=RAND_MAX;//随机数的最大值 int q; q = rand();//可以通过乘除数量级来限制某个区间的随机数 cout << p< #include
头文件 srand((unsigned)time(NULL));
q = rand();
#include
#include
#include
using namespace std;
/* 要生成和返回随机数的函数 */
int* getRandom()//定义指针型函数,返回类型为指针
{
static int r[10];//定义局部变量为静态变量
int i;
/* 设置种子 */
srand((unsigned)time(NULL));
for (i = 0; i < 10; ++i)
{
r[i] = rand();
printf("r[%d] = %d\n", i, r[i]);
//产生随机数组
}
return r; //指定不带索引的数组名来返回一个指向数组的指针
}
/* 要调用上面定义函数的主函数 */
int main()
{
/* 一个指向整数的指针 */
int* p;
int i;
p = getRandom();//指针接收数组
for (i = 0; i < 10; i++)
{
printf("*(p + %d) : %d\n", i, *(p + i));//遍历指针(数组)
}
return 0;
}
r[0] = 28767
r[1] = 22854
r[2] = 9116
r[3] = 8556
r[4] = 3510
r[5] = 25364
r[6] = 29177
r[7] = 16194
r[8] = 13483
r[9] = 6931
*(p + 0) : 28767
*(p + 1) : 22854
*(p + 2) : 9116
*(p + 3) : 8556
*(p + 4) : 3510
*(p + 5) : 25364
*(p + 6) : 29177
*(p + 7) : 16194
*(p + 8) : 13483
*(p + 9) : 6931
数组名是一个指向数组中第一个元素的常量指针
balance 是一个指向 &balance[0] 的指针,即数组 balance 的第一个元素的地址。因此,下面的程序片段把 p 赋值为 balance 的第一个元素的地址
double *p; double balance[10]; p = balance;
使用数组名作为常量指针是合法的,反之亦然。因此,*(balance + 4) 是一种访问 balance[4] 数据的合法方式。
一旦您把第一个元素的地址存储在 p 中,您就可以使用 *p、*(p+1)、*(p+2) 等来访问数组元素。
#include
#include #include using namespace std; int main() { /* 带有 5 个元素的整型数组 */ double balance[5] = { 1000.0, 2.0, 3.4, 17.0, 50.0 }; double* p; int i; p = balance;//指针p指向balance[0]的位置 /* 输出数组中每个元素的值 */ printf("使用指针的数组值\n"); for (i = 0; i < 5; i++) { printf("*(p + %d) : %f\n", i, *(p + i)); } printf("使用 balance 作为地址的数组值\n"); for (i = 0; i < 5; i++) { printf("*(balance + %d) : %f\n", i, *(balance + i)); } return 0; } 使用指针的数组值 *(p + 0) : 1000.000000 *(p + 1) : 2.000000 *(p + 2) : 3.400000 *(p + 3) : 17.000000 *(p + 4) : 50.000000 使用 balance 作为地址的数组值 *(balance + 0) : 1000.000000 *(balance + 1) : 2.000000 *(balance + 2) : 3.400000 *(balance + 3) : 17.000000 *(balance + 4) : 50.000000
第一个枚举成员的默认值为整型的 0,后续枚举成员的值在前一个成员上加 1。我们在这个实例中把第一个枚举成员的值定义为 1,第二个就为 2,以此类推。
enum season {spring, summer=3, autumn, winter};
没有指定值的枚举元素,其值为前一元素加 1。也就说 spring 的值为 0,summer 的值为 3,autumn 的值为 4,winter 的值为 5
1、先定义枚举类型,再定义枚举变量
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
enum 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
#include
#include
using namespace std;
enum DAY
{
MON = 1, TUE, WED, THU, FRI, SAT, SUN
};
int main()
{
enum DAY day1;
enum DAY day2;
day1 = WED;
day2 = SAT;
cout << day1;
cout << endl;
cout << day2;
return 0;
}
输出结果;
3
6
枚举类型是被当做 int 或者 unsigned int 类型来处理的,所以按照 C 语言规范是没有办法遍历枚举类型的。不过在一些特殊的情况下,枚举类型必须连续是可以实现有条件的遍历。
#include
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
#include
using namespace std;
int main()
{
enum day//定义枚举
{
saturday,
sunday,
monday,
tuesday,
wednesday,
thursday,
friday
} workday;
int a = 1;
enum day weekend;//创建枚举变量
weekend = (enum day)a; //类型转换
//weekend = a; //错误
printf("weekend:%d", weekend);
return 0;
}
- 指针的每一次递增,它其实会指向下一个元素的存储单元。
- 指针的每一次递减,它都会指向前一个元素的存储单元。
- 指针在递增和递减时跳跃的字节数取决于指针所指向变量数据类型长度,比如 int 就是 4 个字节。
假设 ptr 是一个指向地址 1000 的整型指针,是一个 32 位的整数,执行ptr++,在执行完上述的运算之后,ptr 将指向位置 1004,因为 ptr 每增加一次,它都将指向下一个整数位置,即当前位置往后移 4 字节。这个运算会在不影响内存位置中实际值的情况下,移动指针到下一个内存位置。
#include
using namespace std; const int MAX = 3; int main() { int var[] = { 10, 100, 200 }; int i, * ptr; /* 指针中的数组地址 */ ptr = var;//将数组赋值给指针 for (i = 0; i < MAX; i++) { printf("存储地址:var[%d] = %p\n", i, ptr); printf("存储值:var[%d] = %d\n", i, *ptr); /* 指向下一个位置 */ ptr++;//数组中的下一个位置 } return 0; } 存储地址:var[0] = e4a298cc 存储值:var[0] = 10 存储地址:var[1] = e4a298d0 存储值:var[1] = 100 存储地址:var[2] = e4a298d4 存储值:var[2] = 200
指针可以用关系运算符进行比较,如 ==、< 和 >。如果 p1 和 p2 指向两个相关的变量,比如同一个数组中的不同元素,则可对 p1 和 p2 进行大小比较。
#include
using namespace std; const int MAX = 3; int main() { int var[] = { 10, 100, 200 }; int i, * ptr; /* 指针中第一个元素的地址 */ ptr = var; i = 0; while (ptr <= &var[MAX - 1])//判断是否为最后一个数组的地址 { printf("存储地址:var[%d] = %p\n", i, ptr); printf("存储值:var[%d] = %d\n", i, *ptr); /* 指向上一个位置 */ ptr++; i++; } return 0; } 存储地址:var[0] = 0077FC78 存储值:var[0] = 10 存储地址:var[1] = 0077FC7C 存储值:var[1] = 100 存储地址:var[2] = 0077FC80 存储值:var[2] = 200
一个指向指针的指针变量必须如下声明,即在变量名前放置两个星号。
int **var;
#include
int main ()
{
int V;
int *Pt1;
int **Pt2;
V = 100;
/* 获取 V 的地址 */
Pt1 = &V;
/* 使用运算符 & 获取 Pt1 的地址 */
Pt2 = &Pt1;
/* 使用 pptr 获取值 */
printf("var = %d\n", V );
printf("Pt1 = %p\n", Pt1 );
printf("*Pt1 = %d\n", *Pt1 );
printf("Pt2 = %p\n", Pt2 );
printf("**Pt2 = %d\n", **Pt2);
return 0;
}
var = 100
Pt1 = 0x7ffee2d5e8d8
*Pt1 = 100
Pt2 = 0x7ffee2d5e8d0
**Pt2 = 100
函数指针是指向函数的指针变量。
通常我们说的指针变量是指向一个整型、字符型或数组等变量,而函数指针是指向函数。
函数指针可以像一般函数一样,用于调用函数、传递参数。
#include
using namespace std;
int max(int x, int y)
{
return x > y ? x : y;
}
int main(void)
{
/* p 是函数指针 */
int (*p)(int, int) = &max;// 声明一个指向同样参数、返回值的函数指针类型
int a, b, c, d, e;
cout<<"请输入三个数字:";
cin >> a >> b >> c;
e = max(max(a, b), c);
d = p(p(a, b), c);
printf("最大的数字是: %d\n", d);
printf("最大的数字是: %d\n", e);
return 0;
}
字符串实际上是使用 null 字符 \0 终止的一维字符数组。因此,一个以 null 结尾的字符串,包含了组成字符串的字符。
char site[7] = {'R', 'U', 'N', 'O', 'O', 'B', '\0'};
由于在数组的末尾存储了空字符,所以字符数组的大小比单词 RUNOOB 的字符数多一个。
序号 | 函数 & 目的 |
---|---|
1 | strcpy(s1, s2); 复制字符串 s2 到字符串 s1。 |
2 | strcat(s1, s2); 连接字符串 s2 到字符串 s1 的末尾。 |
3 | strlen(s1); 返回字符串 s1 的长度。 |
4 | strcmp(s1, s2); 如果 s1 和 s2 是相同的,则返回 0;如果 s1 |
5 | strchr(s1, c); 返回一个指针,指向字符串 s1 中字符 c 的第一次出现的位置。 |
6 | strstr(s1, s2); 返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。 |
#include
using namespace std;
int main()
{
char str1[100] = "runogdfgdfghshgfhfghob";
char str2[100] = "goosacfsaaagle";
cout<
ogdfgdfghshgfhfghob
oosacfsaaagle
struct 结构体名称 {
结构体属性
结构体属性
结构体属性
...
} 结构体变量
三种定义方法:
//此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度的c
//同时又声明了结构体变量s1
//这个结构体并没有标明其标签
struct
{
int a;
char b;
double c;
} s1;
//此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度的c
//结构体的标签被命名为SIMPLE,没有声明变量
struct SIMPLE
{
int a;
char b;
double c;
};
//用SIMPLE标签的结构体,另外声明了变量t1、t2、t3
struct SIMPLE t1, t2[20], *t3;
//也可以用typedef创建新类型
typedef struct
{
int a;
char b;
double c;
} Simple2;
//现在可以用Simple2作为类型声明新的结构体变量
Simple2 u1, u2[20], *u3;
为了访问结构的成员,我们使用成员访问运算符(.)
#include
#include
using namespace std;
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
};
int main()
{
Books Book1; /* 声明 Book1,类型为 Books */
Books Book2; /* 声明 Book2,类型为 Books */
Book1.author;//调用
Book2.book_id;
return 0;
}
利用指针访问
定义指向结构的指针
struct Books *struct_pointer;
在上述定义的指针变量中存储结构变量的地址
struct_pointer = &Book1;
为了使用指向该结构的指针访问结构的成员,您必须使用 -> 运算符
struct_pointer->title;
#include
using namespace std;
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
};
void diao(Books* p) {
cout << p->title << endl;
cout << p->author << endl;
cout << p->subject << endl;
cout << p->book_id << endl;
}
int main()
{
Books Book1; /* 声明 Book1,类型为 Books */
Books Book2; /* 声明 Book2,类型为 Books */
diao(&Book1);//传入
return 0;
}
typedef 关键字,您可以使用它来为类型取一个新的名字
typedef char BYTE;
在这个类型定义之后,标识符 BYTE 可作为类型 unsigned char 的缩写
BYTE b1, b2;
也可以使用 typedef 来为用户自定义的数据类型取一个新的名字
typedef struct Books { .... } Book;//Book是Books的别名 int main( ) { Book book; return 0; }
typedef vs #define
typedef 仅限于为类型定义符号名称
#define 不仅可以为类型定义别名
getchar(void) 函数从屏幕读取下一个可用的字符,并把它返回为一个整数。这个函数在同一个时间内只会读取一个单一的字符。
putchar(c) 函数把整数c对应的字符输出到屏幕上,并返回相同的字符。这个函数在同一个时间内只会输出一个单一的字符。
#include
using namespace std;
int main()
{
int c;
printf("Enter a value :");
c = getchar();
printf("\nYou entered: ");
putchar(c);
printf("\n");
cout << c << endl;
return 0;
}
char *gets(char *s){char *s 表示s是一个字符串数组,也可以直接用数组表示} 函数从输入读取一行到 s,直到一个终止符或 EOF。
puts(const char *s) 函数把字符串 s 和一个尾随的换行符输出
#include
int main( )
{
char str[100];
printf( "Enter a value :");
gets( str );
printf( "\nYou entered: ");
puts( str );
return 0;
}
fopen( ) 函数来创建一个新的文件或者打开一个已有的文件
FILE *fopen( const char * filename, const char * mode );
访问模式 mode 的值可以是下列值中的一个:
r | 打开一个已有的文本文件,允许读取文件。 |
w | 打开一个文本文件,允许写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会从文件的开头写入内容。如果文件存在,则该会被截断为零长度,重新写入。 |
a | 打开一个文本文件,以追加模式写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会在已有的文件内容中追加内容。 |
r+ | 打开一个文本文件,允许读写文件。 |
w+ | 打开一个文本文件,允许读写文件。如果文件已存在,则文件会被截断为零长度,如果文件不存在,则会创建一个新文件。 |
a+ | 打开一个文本文件,允许读写文件。如果文件不存在,则会创建一个新文件。读取会从文件的开头开始,写入则只能是追加模式。 |
如果处理的是二进制文件,则需使用下面的访问模式来取代上面的访问模式:
"rb", "wb", "ab", "rb+", "r+b", "wb+", "w+b", "ab+", "a+b"
关闭文件,请使用 fclose( ) 函数
int fclose( FILE *fp );
如果成功关闭文件,fclose( ) 函数返回零,如果关闭文件时发生错误,函数返回 EOF。
int fputc( int c, FILE *fp );
函数 fputc() 把参数 c 的字符值写入到 fp 所指向的输出流中。如果写入成功,它会返回写入的字符,如果发生错误,则会返回 EOF。
int fputs( const char *s, FILE *fp );
函数 fputs() 把字符串 s 写入到 fp 所指向的输出流中。如果写入成功,它会返回一个非负值,如果发生错误,则会返回 EOF。
int fprintf(FILE *fp,const char *format, ...) //*format代表字符串数组
函数把一个字符串写入到文件中。
#include
int main()
{
FILE *fp = NULL;
fp = fopen("/tmp/test.txt", "w+");
fprintf(fp, "This is testing for fprintf...\n");
fputs("This is testing for fputs...\n", fp);
fclose(fp);
}
当上面的代码被编译和执行时,它会在 /tmp 目录中创建一个新的文件 test.txt,并使用两个不同的函数写入两行。接下来让我们来读取这个文件。
fgetc() 函数从 fp 所指向的输入文件中读取一个字符。返回值是读取的字符,如果发生错误则返回 EOF。
int fgetc( FILE * fp );
函数 fgets() 从 fp 所指向的输入流中读取 n - 1 个字符。它会把读取的字符串复制到缓冲区 buf,并在最后追加一个 null 字符来终止字符串。如果这个函数在读取最后一个字符之前就遇到一个换行符 '\n' 或文件的末尾 EOF,则只会返回读取到的字符,包括换行符。
char *fgets( char *buf, int n, FILE *fp );
函数来从文件中读取字符串,但是在遇到第一个空格和换行符时,它会停止读取。
int fscanf(FILE *fp, const char *format, ...)
#include
int main()
{
FILE *fp = NULL;
char buff[255];
fp = fopen("D:/test.txt", "r");
fscanf(fp, "%s", buff);
printf("1: %s\n", buff );
fgets(buff, 255, (FILE*)fp);//接着第一个读取结束的位置开始
printf("2: %s\n", buff );
fgets(buff, 255, (FILE*)fp);
printf("3: %s\n", buff );
fclose(fp);
}
fscanf() 读取到abcdff遇到空格结束
第一个fgets 读到换行符
第二个fgets 读到换行符
宏 | 描述 |
---|---|
__DATE__ | 当前日期,一个以 "MMM DD YYYY" 格式表示的字符常量。 |
__TIME__ | 当前时间,一个以 "HH:MM:SS" 格式表示的字符常量。 |
__FILE__ | 这会包含当前文件名,一个字符串常量。 |
__LINE__ | 这会包含当前行号,一个十进制常量。 |
__STDC__ | 当编译器以 ANSI 标准编译时,则定义为 1。 |
#include
main()
{
printf("File :%s\n", __FILE__ );
printf("Date :%s\n", __DATE__ );
printf("Time :%s\n", __TIME__ );
printf("Line :%d\n", __LINE__ );
printf("ANSI :%d\n", __STDC__ );
}
#include
这种形式用于引用系统头文件。它在系统目录的标准列表中搜索名为 file 的文件。
#include "file"
这种形式用于引用用户头文件。它在包含当前文件的目录中搜索名为 file 的文件。
一个函数可以调用其自身。但在使用递归时,程序员需要注意定义一个从函数退出的条件,否则会进入死循环。
void recursion() { statements; ... ... ... recursion(); /* 函数调用自身 */ ... ... ... } int main() { recursion(); }
int func(int(参数的个数), ... )
{
.
.
.
}
int main()
{
func(2, 2, 3);
func(3, 2, 3, 4);
}
函数 func() 最后一个参数写成省略号,即三个点号(...),省略号之前的那个参数是 int,代表了要传递的可变参数的总数
使用 stdarg.h 头文件,该文件提供了实现可变参数功能的函数和宏
- 定义一个函数,最后一个参数为省略号,省略号前面可以设置自定义参数。
- 在函数定义中创建一个 va_list 类型变量,该类型是在 stdarg.h 头文件中定义的。
- 使用 int 参数和 va_start 宏来初始化 va_list 变量为一个参数列表。宏 va_start 是在 stdarg.h 头文件中定义的。
- 使用 va_arg 宏和 va_list 变量来访问参数列表中的每个项。
- 使用宏 va_end 来清理赋予 va_list 变量的内存。
#include
#include
double average(int num,...)
{
va_list valist;
double sum = 0.0;
int i;
/* 为 num 个参数初始化 valist */
va_start(valist, num);
/* 访问所有赋给 valist 的参数 */
for (i = 0; i < num; i++)
{
sum += va_arg(valist, int);
}
/* 清理为 valist 保留的内存 */
va_end(valist);
return sum/num;
}
int main()
{
printf("Average of 2, 3, 4, 5 = %f\n", average(4, 2,3,4,5));
printf("Average of 5, 10, 15 = %f\n", average(3, 5,10,15));
}