存储类定义了程序中变量、函数的可见性和生命周期。
存储类 | 说明 |
---|---|
auto |
是局部变量的默认存储类, 限定变量只能在函数内部使用; |
register |
代表了寄存器变量,不在内存中使用; |
static |
是全局变量的默认存储类,表示变量在程序生命周期内可见; |
extern |
表示全局变量,即对程序内所有文件可见,类似于Java中的public关键字; |
指针
指针变量
指针是个变量,存放内存单元的地址。
#include <stdio.h>
void main() {
int a = 10; //在内存中开辟一块空间
int *p = &a; //对变量a取址,赋值给指针变量p
}
野指针——就是指针指向的位置是不可知的。指针变量如果在定义时未初始化,其值是随机的,这意味着此时的解引用,是去访问一个不确定的地址,其结果是不可知的。
空指针——指针的值为NULL。
指针类型
指针类型,决定了指针每移动一次所跨越的字节。
#include<stdio.h>
void main(){
int a=3;
int *p=&a;
printf("%p %p",p,p+1);
}
//000000000061FE14 000000000061FE18
//移动了4个字节
// type * 会定义一个变量为指针类型
#include<stdio.h>
int main(){
int a=3;
short *p=(short*)&a;
printf("%p %p",p,p+1);
}
//000000000061FE14 000000000061FE16
//移动了2个字节
//(type *)是指针的类型转变
指针解引用,是在访问内存
#include<stdio.h>
int main(){
int a=3;
int* p= &a;
*p=2;
printf("%d",a);
}
//可以看见定义时*符号可以在type与p间随意放
//解引用会访问指针类型相关的字节
char* pstr="hello";
while(*pstr){
printf("%c\n",*string++);
}
为什么要用指针?
void nicejob(int a){
a=a*2
}
//函数对传递参数的操作仅仅是对拷贝的操作
//这时使用指针来改变函数外的值
void nicejob(int* a){
*a=*a*2
}
//*a通过解引用访问内存
随便找个变量放地址是可行的,如
int p=&a;
但为什么,还麻烦的用int * p
呢?
为什么使用指针类型:
1.不容易混淆
2.指针类型给出了指针改变的实际字节偏移量
二级指针,函数指针
一级指针用途:
- 可以利用一级指针改变函数外部的变量,还可以跨进程改变变量(外挂就是这么搞的)。
- 一级指针可以存储数组的首地址,并通过指针或者下标访问元素。
- 一级指针作为函数的返回值,返回地址;注意:不能返回指向栈的地址,因为函数执行完,变量就被回收了。
- 函数的参数有副本机制,传递实参时,新建一个变量,容纳传递过来的实参。特例:数组在函数参数中拒绝副本机制,数组作为参数时数组的首地址。
- return也有副本机制,返回值保存在CPU寄存器中,因此函数的返回值不能取地址。
- 为什么指针要有类型,因为类型可以让编译器知道如何解析,还有步长。
二级指针,在二维数组中常见:array[2][1]
等价的指针表示法:*(*(array+2)+1)
函数指针:
#include <stdio.h>
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;
printf("请输入三个数字:");
scanf("%d %d %d", & a, & b, & c);
d = p(p(a, b), c); //等价d = max(max(a, b), c)
printf("最大的数字是: %d\n", d);
return 0;
}
结构指针
#include<stdio.h>
#define LEN 20
struct names { //结构声明
char first[LEN];
char last[LEN];
};
struct guy {
struct names handle; //嵌套结构
float income;
};
int main(){
struct guy fellow[2]={ //结构数组变量
{
{"li","hua"},
12000.00
},
{
{"liu","bei"},
23000.00
}
};
struct guy* him; //指向结构的指针
printf("address #1: %p #2: %p\n",&fellow[0],&fellow[1]);
him=&fellow[0];
printf("pointer #1: %p #2: %p\n",him,him+1);
printf("him->income is $%.2f\n(*him).income is $%.2f",him->income,(*him).income);
return 0;
// address #1: 000000000061FDC0 #2: 000000000061FDEC
// pointer #1: 000000000061FDC0 #2: 000000000061FDEC
// him->income is $12000.00
// (*him).income is $12000.00
}
字符串
定义:以 null
字符 '\0'
终止的一维字符数组。
<string.h>
中常用函数
函数 | 作用 |
---|---|
strcpy(s1,s2) |
复制s2到s1 |
strcat(s1, s2) |
连接s2到s1末尾 |
strlen() |
|
strcmp(s1, s2) |
比较s1、s2大小 |
strchr(s1, ch) |
返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。 |
strstr(s1, s2) |
返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。 |
void *memset(void * str,int c,size_t n) |
复制字符c 到str所指向的的前n个字符 |
stdio.h
函数原型 | 说明 |
---|---|
int fclose(FILE *stream) |
关闭流 stream。刷新所有的缓冲区。 |
int fflush(FILE *stream) |
刷新流 stream 的输出缓冲区。 |
void rewind(FILE *stream) |
设置文件位置为给定流 stream 的文件的开头。 |
int printf(const char *format, ...) |
发送格式化输出到标准输出 stdout。 |
int sprintf(char *str, const char *format, ...) |
发送格式化输出到字符串。 |
int fgetc(FILE *stream) |
从指定的流 stream 获取下一个字符(一个无符号字符),并把位置标识符往前移动。 |
char *fgets(char *str, int n, FILE *stream) |
从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内。当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。 |
int snprintf(char *str, size_t size, const char *format, ...) |
格式字符串到 str 中。 |
输入 | 输出 | 说明 |
---|---|---|
scanf() |
printf() |
|
fscanf(stdin,...) |
fprintf(stdout,...) |
|
getchar() |
putchar() |
|
gets() |
puts() |
遇到换行符结束 |
int getc(FILE *stream) |
从文件读取字符,fgetc与getc差不多 | |
fgets(str,n,fp) |
fputs(str,fp) |
最多只能读入n-1个字符到str,可存储\n |
gets_s(words,STLEN) |
||
fprintf() |
int fprintf(FILE *stream, const char *format, ...) |
//snprintf
#include <stdio.h>
int main()
{
char buffer[50];
char* s = "runoobcom";
// 读取字符串并存储在 buffer 中
int j = snprintf(buffer, 6, "%s\n", s);
// 输出 buffer及字符数
printf("string:%s \n character count = %d\n", buffer, j);
//不能完全存入时snprintf返回-1
return 0;
}
stdlib.h
函数原型 | 说明 |
---|---|
double atof(const char *str) |
把参数 str 所指向的字符串转换为一个浮点数(类型为 double 型) |
int atoi(const char *str) |
|
void *calloc(size_t nitems, size_t size) |
分配所需的内存空间,并返回一个指向它的指针 |
void *malloc(size_t size) |
malloc 和 calloc 之间的不同点是,malloc 不会设置内存为零,而 calloc 会设置分配的内存为零。 |
void free(void *ptr) |
释放内存 |
void exit(int status) |
立即终止调用进程??? |
void qsort(void *base,nitems,size,func |
size_t 在这是一个库定义的变量类型,无符号整型。
关于动态内存:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char *str;
/* 最初的内存分配 */
str = (char *) malloc(15); //强制指针类型转变,读度1
strcpy(str, "runoob");
printf("String = %s, Address = %u\n", str, str);
//String = runoob, Address = 3662685808
/* 重新分配内存 */
str = (char *) realloc(str, 25);
strcat(str, ".com");
printf("String = %s, Address = %u\n", str, str);
//String = runoob.com, Address = 3662685808
free(str);
return(0);
}
文件读写
挑一段代码看看,,,,
#include<stdio.h>
#include<string.h>
int main()
{
FILE *fp; //创建 (文件指针类型) 变量
char str[3][30],temp[30];
int i,j,k,n=3;
printf("input your strings:\n");
for(i=0;i<n;i++)
gets(str[i]);
for(i=0;i<n-1;i++)
{
k=i;
for(j=i+1;j<n;j++)
if(strcmp(str[k],str[j])>0)
k=j;
if(k!=i)
{
strcpy(temp,str[i]);
strcpy(str[i],str[k]);
strcpy(str[k],temp);
}
} //字符排序
//函数原型 FILE *fopen( const char * filename, const char * mode );
//单目运算符的优先级比双目的高
if((fp=fopen("C:\\intel\\c.txt","ab+"))==NULL)
{
printf("\nCannot open file\nstrike any key exit\n");
getchar();
return 1;
}
printf("\nthe new:\n");
for(i=0;i<n;i++)
{
fputs(str[i],fp);
fputs("\n",fp); //写入内容
printf("%s\n",str[i]);
}
fclose(fp); //关闭文件
return 0;
}
预处理器
C预处理器不是编译器的组成部分,而是一个文本替换的工具
指令 | 描述 |
---|---|
#define |
定义宏 |
#include |
包含一个源代码文件 |
#undef |
取消已定义的宏 |
#ifdef |
如果宏已经定义,则返回真 |
#ifndef |
如果宏没有定义,则返回真 |
#if |
如果给定条件为真,则编译下面代码 |
#else |
#if 的替代方案 |
#elif |
如果前面的 #if 给定条件不为真,当前条件为真,则编译下面代码 |
#endif |
结束一个 #if……#else 条件编译块 |
#error |
当遇到标准错误时,输出错误消息 |
#pragma |
使用标准化方法,向编译器发布特殊的命令到编译器中 |
//一个课堂上的例子,避免函数库的嵌套调用
#pragma once
#ifndef __slnname__g_A__
#define __slnname_g_A__
int g_A = 1;
#endif
命令行参数
命令行参数是使用 main()
函数参数来处理的,其中,argc
是指传入参数的个数,argv[]
是一个指针数组,指向传递给程序的每个参数。
argv[0]
= _____.exe,argv[1]
= 参数一,argv[2]
= 参数二 ………- 项目属性,调试中,可以更改命令行参数。cmd 中可直接输入命令行参数,如
a.exe argv1
- 程序中
argc
,argv
已经是确定的值。
题目
- 分别统计纯英文文本文件中每一个字符个数、单词总的个数;要求统计结果输出到另外一个文件中,源文件和结果文件名均由命令行参数指定.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#pragma warning(disable:4996)
void main(int argc, char *argv[]) {
char *in = argv[1];
char *out = argv[2];
char c;
FILE *fpout, *fpin;
int cnt = 0;
if ((fpin = fopen(in, "r")) == NULL) {
printf("输入文件打开失败\n");
return;
}
int count[128] = { 0 };
c = fgetc(fpin);
int flag = 1; //flag表示读到字母或数字时是否可以cnt++
while (c != EOF) {
if ( (('0' <= c) && (c <= '9')) || (('a' <= c) && (c<= 'z')) || (('A' <= c) && (c <= 'Z')) ){
if (flag)
cnt++;
flag = 0;
}
else {
flag = 1;
}
printf("%d %c\n", flag,c);
count[c] += 1;
c = fgetc(fpin);
}
fclose(fpin);
if ((fpout = fopen(out, "w")) == NULL) {
printf("输出文件打开失败\n");
exit(0);
}
fprintf(fpout, "单词个数为: %d\n", cnt);
fprintf(fpout, "\n===================================\n\n");
fprintf(fpout, "字符 \t 个数\n");
for (int i = 0; i < 128; i++) {
if (count[i] > 0)
fprintf(fpout, "%3c: \t %d\n", i, count[i]);
}
fclose(fpout);
}