C语言



存储类定义了程序中变量、函数的可见性和生命周期。

存储类 说明
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[] 是一个指针数组,指向传递给程序的每个参数。

  1. argv[0] = _____.exe,argv[1] = 参数一,argv[2] = 参数二 ………
  2. 项目属性,调试中,可以更改命令行参数。cmd 中可直接输入命令行参数,如a.exe argv1
  3. 程序中argcargv已经是确定的值。

题目

  1. 分别统计纯英文文本文件中每一个字符个数单词总的个数;要求统计结果输出到另外一个文件中,源文件和结果文件名均由命令行参数指定.
#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);
}

文章作者: ╯晓~
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 ╯晓~ !
评论
  目录