• 程序设计入门-C语言基础知识-翁恺-第七周:指针与字符串-详细笔记(七)


    第七周:指针与字符串

    7.1 指针初步

    sizeof

    • 是一个运算符,给出某个类型或变量在内存中所占据的字节数
      • sizeof(int)
      • sizeof(i)

    运算符 &

    • scanf("%d",&i);
    • 获得变量的地址,它的操作必须是变量
      • int i;printf("%p",&i);
    • 地址的大小与int是否相同取决于编译器,地址和整数不一定是相同的,这取决于你的系统架构。
    void pointerDemo(void)
    {
        int i = 0;
        int p;
        p = (int)&i;
        printf("0x%x
    ", p); 
        printf("%p
    ", &i);  
        printf("%lu
    ", sizeof(int)); //32位系统为4 64位系统为4
        printf("%lu
    ", sizeof(&i));//32位系统为4 64位系统为8 
    }
    

    数组与地址:

    #include <stdio.h>
    
    int main(int argc, char *argv[])
    {
     int a[10];
    
     printf("%p
    ", &a);
     printf("%p
    ", a);
     printf("%p
    ", &a[0]);
     printf("%p
    ", &a[1]);
     return 0;
    }
    
    

    运行结果:

    000000000069FE20
    000000000069FE20
    000000000069FE20
    000000000069FE24
    
    --------------------------------
    Process exited after 0.1564 seconds with return value 0
    
    

    scanf

    • 如果能够将取得的变量的地址传递给一个函数,能否通过这个地址在那个函数内访问这个变量?
      • scanf("%d", &i);
    • scanf()的原型应该是怎样的?我们需要一个参数能保存别的变量的地址,如何表达能够保存地址的变量?

    指针

    • 就是保存地址的变量
      int i;
      int* p = &i;
      int* p,q;
      int p,q;//p是一个指针指向int,q是一个int类型变量,换句话说p是一个int,因此int *p就是一个指向int的指针。
    • 指针变量的值是内存的地址
    • 普通变量的值是实际的值
    • 指针变量的值是具有实际值的变量的地址

    作为参数的指针

    • void f(int *p);
    • 在被调用的时候得到了某个变量的地址:
      • int i=0;f(&i);
    • 在函数里可以通过这个指针访问外面的这个i

    访问那个地址上的变量*

    • *是一个单目运算符,用来访问指针的值所表示的地址上的变量
    • 可以做右值也可以做左值
      • int k=*p;
      • *p=k+1;

    传入函数的数组成了什么?

    int isPrime(int x, int knownPrimes[], int numberOfKonwPrimes){
        //...
    }
    
    • 函数参数表中的数组实际上是指针
      • sizeof(a) == sizeof(int*)
      • 但是可以用数组的运算符[]进行操作

    数组参数

    • 以下四种函数原型是等价的:
      • int sum(int *arr, int n);
      • int sum(int *, int n);
      • int sum(int arr[], int n);
      • int sum(int [], int);

    数组变量是特殊的指针

    • 数组变量本身表达地址,所以
      • int a[10]; int *p=a; //无需用&取地址
      • 但是数组的单元表达的是变量,需要用&取地址
      • a == &a[0];
    • []运算符可以对数组做,也可以对指针做:
      • p[0] <==> a[0]
    • *运算符可以对指针做,也可以对数组做:
      • *a = 25;
    • 数组变量是const的指针,所以不能被赋值
      • int a[] <==>(等价于) int * const a

    7.2 字符类型

    字符类型

    • char是一种整数,也是一种特殊的类型:字符。这是因为:
      • 用单引号表示的字符字面量:'a','1'
      • ''也是一个字符
      • printf和scanf里面用%c来输入输出字符

    字符的输入输出

    • 如何输入'1'这个字符给char c?
      • scanf("%c", &c); -> 1
      • scanf("%d", &i); c=i; -> 49
    • '1'的ASCII编码是49,所以当c==49时,它代表字符'1'
    #include <stdio.h>
    
    int main(int argc, char *argv[])
    {
     char c;
     scanf("%c", &c);
     printf("c=%d
    ", c);
     printf("c='%c'
    ", c);
     return 0;
    }
    
    

    输出结果:

    1
    c=49
    c='1'
    
    --------------------------------
    Process exited after 0.9084 seconds with return value 0
    

    字符计算

    • 一个字符加一个数字得到ASCII码表中那个数之后的字符
      • char c ='A';c++;printf("%c ", c); // B
    • 两个字符相减,得到它们在表中的距离

    大小写转换

    • 字母在ASCII表中是顺序排列的
    • 大写字母和小写字母是分开排列的,并不在一起
    • 'a'-'A'可以得到两段之间的距离,于是
      • a+'a'-'A'可以把一个大写字母变成小写字母
      • a+'A'-'a'可以把一个小写字母变成大写字母
    #include <stdio.h>
    
    int main(int argc, char *argv[])
    {
     char c = 'e';
     printf("a=%d
    ", 'a');
     printf("A=%d
    ", 'A');
     printf("a-A=%d
    ", 'a' - 'A');
     printf("A-a=%d
    ", 'A' - 'a');
     c = c + 'A' - 'a';
     printf("c+A-a=%c
    ", c);
     c = c + 'a' - 'A';
     printf("c+A-a=%c
    ", c);
     return 0;
    }
    
    
    a=97
    A=65
    a-A=32
    A-a=-32
    c+A-a=E
    c+A-a=e
    
    --------------------------------
    Process exited after 0.1735 seconds with return value 0
    

    逃逸字符

    • 用来表达无法打印出来得到控制字符或特殊字符,它由一个反斜杠""开头,后面跟上另一个字符,这两个字符合起来,组成了一个字符
      • printf("请分别输入身高的英尺和英寸,"
        "如输入"5 7"表示5英尺7英寸");

    制表位

    • 每行的固定位置,不是代表固定大小的字符数量。
    • 一个 使得输出从下一个制表位开始
    • 用 才能使得上下两行对齐
      • printf("123 456 ");
      • printf("12 456 ");
      • 以上两行在一个位置。
    123      456
    12        456
    
    --------------------------------
    Process exited after 0.222 seconds with return value 0
    

    回车和换行

    • 在早期的打字机中回车和换行是两个动作
    • 回车是将输入的位置从页面的最右边挪动到最左边,换行则是新起一行
    • 大部分shell会把 翻译为

    7.3 字符串

    • char word[] = {'H','e','l','l','o','!',''};

    • ''可以看作整数0
    • 以0(整数0)结尾的一串字符
      • 0或''是一样的,但是和'0'不同,''一定是一个字节的变量,而整数0是4个字节
    • 0标志字符串的结束,但它不是字符串的一部分
      • 计算字符串长度的时候不包含这个0
    • 字符串在内存中是以数组的形式存在,以数组或指针的形式访问
      • 更多是以指针的形式
    • string.h 里有很多处理字符串的函数

    字符串变量

    • char *str = "Hello";
    • char word[] = "Hello";
    • char line[10] = "Hello";

    字符串常量(字符串字面量)

    • "Hello"
    • "Hello"会被编译器变成一个字符数组放在某处,这个数组的长度是6,结尾还有表示结束的0
    • 两个相邻的字符串常量会被自动连接起来

    字符串

    • C语言的字符串是以字符数组的形式存在的
      • 不能用运算符对字符串做运算
      • 通过数组的方式可以遍历字符串
    • 唯一特殊的地方是字符串字面量可以用来初始化字符数组
    • 以及标准库提供了一系列字符串函数
    • 在C语言诞生的年代,计算机更多的是用来做数字计算而不是信息处理,因此C语言没有为字符串专门设计一个字符串类型。

    字符串输入输出

    • char string[8];
    • scanf("%s", string);
    • printf("%s", string);
    • scanf读入一个单词(到空格、tab或回车为止)
    • scanf是不安全的,因为不知道要读入的内容长度
      • scanf("%7s",string); 只读7个字符
    • 在%和s之间的数字表示最多允许读入的字符串数量,这个数字应该比数组的大小小一
      • 下一个scanf从哪里开始?
      • 当第一个scanf读够了7个字符,溢出的字符就会交由第二个scanf读取了。

    常见错误

    • char *string;
    • scanf("%s", string);
    • 指针没有没初始化就使用,可能导致程序出错。
    • 由于没有对string初始化,使用的是程序中已有内存的任一地方,所以不一定每次运行都出错,但是当那个内存有很重要的东西的时候,程序可以就出错了。

    空字符串

    • char buffer[100] = "";
      • 这是一个空的字符串,buffer[0] == ''
    • char buffer[] = "";
      • 这个数组的长度只有1!
      • 因此这个字符串放不下任何的字符。

    string.h

    • strlen
    • strcmp
    • strcpy
    • strcat
    • strchr
    • strstr

    strlen

    • size_t strlen(const char *s);
    • 返回s的字符串长度(不包括结尾的0)

    strcmp

    • int strcmp(const char *s1, const char *s2);
    • 比较两个字符,返回
      • 0:s1==s2
      • 1:s1>s2
      • -1:s1<s2

    strcpy

    • char *strcpy(char *restrict dst, const char *restrict src);
    • 把src的字符串拷贝到dst
      • restrict表名src和dst不重叠(C99)
    • 返回dst
      • 为了能链起代码来

    strcat

    • char * strcat(char *restrict s1, const *restrict s2);
    • 把s2拷贝到s1的后面,接成一个长的字符串
    • 返回s1
    • s1必须有足够的空间

    安全问题

    • strcpy和strcat都可能出现安全问题
      • 如果目的地没有足够的空间?

    安全版本

    • char * strncpy(...) 只拷贝多少个长度的字符
    • char * strncat(...) 只链接多少个字符的长度
    • int strncmp(...) 只比较前几个字符

    字符串中找字符

    • char * strchar(const char *s, int c); //从左边开始找第一个出现的字符
    • char * strrchr(const char *s,int c); //从右边开始找第一个出现字符
    • 返回NULL表示没有找到

    7.3 课后练习

    1、题目内容: 题目内容:
    你的程序要读入一行文本,其中以空格分隔为若干个单词,以‘.’结束。你要输出这行文本中
    每个单词的长度。这里的单词与语言无关,可以包括各种符号,比如“it's”算一个单词,长
    度为 4。注意,行中可能出现连续的空格。
    输入格式:
    输入在一行中给出一行文本,以‘.’结束,结尾的句号不能计算在最后一个单词的长度内。
    输出格式:
    在一行中输出这行文本对应的单词的长度,每个长度之间以空格隔开,行末没有最后的空格。
    输入样例:
    It's great to see you here.
    输出样例:
    4 5 2 3 3 4

    题目分析:

    • 以空格分割单词计数,输出每个单词的长度
    • 遇到.或者字符串结束'' 停止程序

    程序实现:

    #include <stdio.h>
    #include "inputText.h"
    
    
    
    int main(int argc, char *argv[])
    {
        inputALineOfText();
     return 0;
    }
    
    
    #ifndef inputText_h
    #define inputText_h
    
    #include <stdio.h>
    #include <string.h>
    void inputALineOfText();
    #endif
    
    
    #include "inputText.h"
    
    void inputALineOfText(){
     char str[500];
     printf("Please input a line of text:");
     gets(str);
     printf("Your input text's word length respectively are:
    ");
     int wordCount=0;
     for(int i=0;i<sizeof(str)/sizeof(str[0]);i++){
      //printf("'%c',",str[i]);
      if(str[i]==' '){//单词边界 
       if(wordCount>0){
        printf("%d ",wordCount);
       }    
       wordCount = 0;//重新计数 
      }
      else if(str[i]=='.'){
       printf("%d",wordCount);   
       break;
      }
      else if(str[i]==''){
       printf("%d",wordCount);   
       break;
      }else{
       wordCount++; 
      }
     }
      
    }    
    

    测试样例:

    Please input a line of text:It's great to see you here.
    Your input text's word length respectively are:
    4 5 2 3 3 4
    --------------------------------
    Process exited after 13.37 seconds with return value 0
    

    2、 题目内容:
    NMEA-0183 协议是为了在不同的 GPS(全球定位系统)导航设备中建立统一的 BTCM
    (海事无线电技术委员会)标准,由美国国家海洋电子协会(NMEA-The National Marine
    Electronics Associa-tion)制定的一套通讯协议。GPS 接收机根据 NMEA-0183 协议
    的标准规范,将位置、速度等信息通过串口传送到 PC 机、PDA 等设备。
    NMEA-0183 协议是 GPS 接收机应当遵守的标准协议,也是目前 GPS 接收机上使用最
    广泛的协议,大多数常见的 GPS 接收机、GPS 数据处理软件、导航软件都遵守或者至少兼
    容这个协议。
    NMEA-0183 协议定义的语句非常多,但是常用的或者说兼容性最广的语句只有
    $GPGGA、$GPGSA、$GPGSV、$GPRMC、$GPVTG、$GPGLL 等。
    其中$GPRMC 语句的格式如下:
    $GPRMC,024813.640,A,3158.4608,N,11848.3737,E,10.05,324.27,1
    50706,,,A50
    这里整条语句是一个文本行,行中以逗号“,”隔开各个字段,每个字段的大小(长度)不一,
    这里的示例只是一种可能,并不能认为字段的大小就如上述例句一样。
    字段 0:$GPRMC,语句 ID,表明该语句为 Recommended Minimum Specific
    GPS/TRANSIT Data(RMC)推荐最小定位信息
    字段 1:UTC 时间,hhmmss.sss 格式
    字段 2:状态,A=定位,V=未定位
    字段 3:纬度 ddmm.mmmm,度分格式(前导位数不足则补 0)
    字段 4:纬度 N(北纬)或 S(南纬)
    字段 5:经度 dddmm.mmmm,度分格式(前导位数不足则补 0)
    字段 6:经度 E(东经)或 W(西经)
    字段 7:速度,节,Knots
    字段 8:方位角,度
    字段 9:UTC 日期,DDMMYY 格式
    字段 10:磁偏角,(000 - 180)度(前导位数不足则补 0)
    字段 11:磁偏角方向,E=东 W=西
    字段 16:校验值
    这里,“
    ”为校验和识别符,其后面的两位数为校验和,代表了“$”和“”之间所有字符
    (不包括这两个字符)的异或值的十六进制值。上面这条例句的校验和是十六进制的 50,
    也就是十进制的 80。
    提示:运算符的作用是异或。将$和*之间所有的字符做运算(第一个字符和第二个字符异
    或,结果再和第三个字符异或,依此类推)之后的值对 65536 取余后的结果,应该和
    后面
    的两个十六进制数字的值相等,否则的话说明这条语句在传输中发生了错误。注意这个十六
    进制值中是会出现 A-F 的大写字母的。另外,如果你需要的话,可以用 sscanf(s,"%d", &i)
    从字符串 s 中得到其所表达的整数数字给 i。
    现在,你的程序要读入一系列 GPS 输出,其中包含$GPRMC,也包含其他语句。在数据的
    最后,有一行单独的
    END
    表示数据的结束。
    你的程序要从中找出$GPRMC 语句,计算校验和,找出其中校验正确,并且字段 2 表示已
    定位的语句,从中计算出时间,换算成北京时间。一次数据中会包含多条$GPRMC 语句,
    以最后一条语句得到的北京时间作为结果输出。
    你的程序一定会读到一条有效的$GPRMC 语句。
    输入格式:
    多条 GPS 语句,每条均以回车换行结束。最后一行是 END 三个大写字母。
    输出格式:
    6 位数时间,表达为:
    hh:mm:ss
    其中,hh 是两位数的小时,不足两位时前面补 0;mm 是两位数的分钟,不足两位时前面
    补 0;ss 是两位数的秒,不足两位时前面补 0。
    输入样例:
    $GPRMC,024813.640,A,3158.4608,N,11848.3737,E,10.05,324.27,1507
    06,,,A*50
    END
    输出样例:
    10:48:13

    题目分析:

    • 将$和*之间所有的字符做^运算之后的值对 65536 取余后的结果为校验值validCode再取字符串中的校验值与之对比
    • 如果一致,再取出utc时间转换为北京时间
    • 否则输出无效的字符
    • 继续读下一个字符串直至用户输入END为止。

    程序实现:

    #include <stdio.h>
    #include "GPSValidate.h"
    
    int main(int argc, char *argv[])
    {
        validateGPS();
     return 0;
    }
    
    #ifndef GPSValidate_h
    #define GPSValidate_h
    
    #include <stdio.h>
    #include <string.h>
    void validateGPS();
    #endif
    
    #include "GPSValidate.h"
    
    
    #include <stdio.h>
    #include <string.h>
     
    /* 十六进制数转换为十进制数 */
    long hexToDec(char *source);
     
    /* 返回ch字符在sign数组中的序号 */
    int getIndexOfSigns(char ch);
     
    
    /* 十六进制数转换为十进制数 */
    long hexToDec(char *source)
    {
        long sum = 0;
        long t = 1;
        int i, len;
     
        len = strlen(source);
        for(i=len-1; i>=0; i--)
        {
            sum += t * getIndexOfSigns(*(source + i));
            t *= 16;
        }  
     
        return sum;
    }
     
    /* 返回ch字符在sign数组中的序号 */
    int getIndexOfSigns(char ch)
    {
        if(ch >= '0' && ch <= '9')
        {
            return ch - '0';
        }
        if(ch >= 'A' && ch <='F') 
        {
            return ch - 'A' + 10;
        }
        if(ch >= 'a' && ch <= 'f')
        {
            return ch - 'a' + 10;
        }
        return -1;
    }
    
    int calculateGPSCode(char gpsStr[],int arrCount){
     if(gpsStr==NULL) 
      return 0;
      
     int gpsCode=0;
     int isBeginCalc = 0; 
     int isFirstCalc = 1;
     //形式参数,sizeof(gpsSrt)的长度始终为8,和实参无关 
     //for(int i=0;i<sizeof(gpsStr)/sizeof(gpsStr[0]);i++){
     for(int i=0;i<arrCount;i++){
      if(gpsStr[i]=='$'){ //开始计算异或值
       isBeginCalc=1;
      }else if(gpsStr[i]=='*' || gpsStr[i]==''){//结束异或值计算 
       break;
      }else{
       if(isBeginCalc==1){
        if(isFirstCalc==1){
         gpsCode = (int)gpsStr[i];
         isFirstCalc=0;
         //printf("%c",gpsStr[i]);     
        }else{
         gpsCode = gpsCode ^	(int)gpsStr[i];
         //printf("^%c",gpsStr[i]);     
        }        
       }
      }
     }
     gpsCode = gpsCode%65536;
     return gpsCode;
    }
    
    void getGPSCode(char *gpsCode ,const char* gpsStr,int arrCount){ 
    	
     if(gpsStr==NULL) 
      return;
       
     int j=0;
     int isBeginGetCode=0; 
     for(int i=0;i<arrCount;i++){
      if(gpsStr[i] == '*'){//*往后为校验码 
       isBeginGetCode=1;
      }
      else if(gpsStr[i] == ''){
       gpsCode[j]='';
       break;
      }
      else{
       if(isBeginGetCode == 1){
        gpsCode[j] = gpsStr[i];
        j++;
       }
      }
     }  
    }
    
    
    void getUTCTime(char *outChar,const char *gpsChar,int length){
     if(gpsChar == NULL)
      return;
       
     int filedIndex=0;
     int j=0; 
     for(int i=0;i<length;i++){
      if(gpsChar[i] == ','){
       filedIndex++; 
      }
      else {
       if(filedIndex == 1) {    
        outChar[j]=gpsChar[i];
        j++;    
       } else if(filedIndex > 1){
        outChar[j] = '';
        break;
       }
      } 
     }  
    } 
    
    int isContainsGPSInfo(char *gpsChar,int length){
     if(gpsChar == NULL)
      return 0;
      
     int isContain=0;
     int filedIndex=0;
     for(int i=0;i<length;i++){
      if(gpsChar[i] == ','){
       filedIndex++; 
      }
      else {
       if(filedIndex == 2) {
        if(gpsChar[i] == 'A'){
         isContain = 1;
         break;
        }
       } else if(filedIndex > 2){
        isContain = 0;
        break;
       }
      } 
     }
    	
     return isContain;
    } 
    
    
    void getBeiJingTime(char *outChar,char *utcTimeChar){
     if(utcTimeChar == NULL)
      return;
     char utcPrefix[10];  
     strtok(utcTimeChar,".");  
    	
     if(utcTimeChar != NULL){
      int utcPrefixInt=0;
      sscanf(utcTimeChar,"%d",&utcPrefixInt);
      int beiJingTimeInt = utcPrefixInt + 80000; 
      char tmp[10];
      sprintf(tmp,"%d",beiJingTimeInt);
      int j=0;
      int colonIndexFirst = 2;
      int colonIndexSecond = 4;  
      
      for(int i=0;i<strlen(tmp);i++,j++){
       if(i==0){
        if(beiJingTimeInt<100000) {//5位 
         outChar[j]='0';
         outChar[++j]=tmp[i];
         colonIndexFirst--;
         colonIndexSecond--;
        }else{
         outChar[j]=tmp[i];
        }    
        } else if(i==colonIndexFirst || i==colonIndexSecond) {
         outChar[j]=':';
         outChar[++j]=tmp[i];
        }
         else if(i==strlen(tmp)-1) {
         outChar[j]=tmp[i];
         outChar[++j]='';
        }
        else{
         outChar[j]=tmp[i];
        }
      } 
      
     }
    } 
    
    void validateGPS(){
     char str[500];
     printf("Please input a GPS text:");
     gets(str);
    	
    	
     while(strcmp(str,"END")!=0){
      printf("%s
    ",str);
      int length = strlen(str);//sizeof(str)/sizeof(str[0]);
      int gpsCode = calculateGPSCode(str,length);  
      printf("Correct gpsCode is %d
    ",gpsCode);
      char realGPSCode[20];
      getGPSCode(realGPSCode,str,length);  
      long realCodeLong = hexToDec(realGPSCode);
      printf("Real GPSCode is %d
    ",realCodeLong);
      
      if(realCodeLong == (long)gpsCode){
       int isContainsGPS = isContainsGPSInfo(str,length);
       printf("isContainGPS:%d 
    ",isContainsGPS);
       if(isContainsGPS == 1){
        char utcTime[20];
        getUTCTime(utcTime,str,length);
        char beiJingTime[10];
        getBeiJingTime(beiJingTime,utcTime);
        printf("utcTime:%s
    ",utcTime); 
        printf("Success!
    "); 
        printf("BeiJingTime is %s
    ",beiJingTime);    
       }      
      }else {
       printf("Invalid GPS reenter:
    ");   
      }    
      gets(str);
     }
    	
        
    }
    

    测试样例:

    Please input a GPS text:$GPRMC,024813.640,A,3158.4608,N,11848.3737,E,10.05,324.27,150706,,,A*50
    $GPRMC,024813.640,A,3158.4608,N,11848.3737,E,10.05,324.27,150706,,,A*50
    Correct gpsCode is 80
    Real GPSCode is 80
    isContainGPS:1
    utcTime:024813
    Success!
    BeiJingTime is 10:48:13
    END
    
    --------------------------------
    Process exited after 13.36 seconds with return value 0
    
  • 相关阅读:
    03 java中的基本数据类型和运算符
    02 Eclipse安装
    01 HelloWorld
    express不是内部或外部命令
    win10 内存或系统资源不足,无法打开PPT
    win 10中解决“此文件在另外一个进程中运行”的问题
    后台查找密码暴力破解
    DVWA--全等级暴力破解(Burte Force)
    DVWA简单搭建
    破解版
  • 原文地址:https://www.cnblogs.com/simple-blog/p/9536605.html
Copyright © 2020-2023  润新知