程序設計入門-C語言基礎知識-翁愷-第七週:指針與字符串-詳細筆記(七)

第七週:指針與字符串

7.1 指針初步

sizeofshell

  • 是一個運算符,給出某個類型或變量在內存中所佔據的字節數
    • 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\n", p); 
    printf("%p\n", &i);  
    printf("%lu\n", sizeof(int)); //32位系統爲4 64位系統爲4
    printf("%lu\n", sizeof(&i));//32位系統爲4 64位系統爲8 
}

數組與地址:安全

#include <stdio.h>

int main(int argc, char *argv[])
{
 int a[10];

 printf("%p\n", &a);
 printf("%p\n", a);
 printf("%p\n", &a[0]);
 printf("%p\n", &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\n", c);
 printf("c='%c'\n", 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\n", 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\n", 'a');
 printf("A=%d\n", 'A');
 printf("a-A=%d\n", 'a' - 'A');
 printf("A-a=%d\n", 'A' - 'a');
 c = c + 'A' - 'a';
 printf("c+A-a=%c\n", c);
 c = c + 'a' - 'A';
 printf("c+A-a=%c\n", 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英寸");

製表位

  • 每行的固定位置,不是表明固定大小的字符數量。
  • 一個\t使得輸出從下一個製表位開始
  • 用\t才能使得上下兩行對齊
    • printf("123\t456\n");
    • printf("12\t456\n");
    • 以上兩行在一個位置。
      ```
      123 456
      12 456

Process exited after 0.222 seconds with return value 0
```
回車和換行

  • 在早期的打字機中回車和換行是兩個動做
  • 回車是將輸入的位置從頁面的最右邊挪動到最左邊,換行則是新起一行
  • 大部分shell會把\n翻譯爲\r\n

7.3 字符串

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

  • '\0'能夠看做整數0
  • 以0(整數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] == '\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 課後練習

一、題目內容: 題目內容:
你的程序要讀入一行文本,其中以空格分隔爲若干個單詞,以‘.’結束。你要輸出這行文本中
每一個單詞的長度。這裏的單詞與語言無關,能夠包括各類符號,好比「it's」算一個單詞,長
度爲 4。注意,行中可能出現連續的空格。
輸入格式:
輸入在一行中給出一行文本,以‘.’結束,結尾的句號不能計算在最後一個單詞的長度內。
輸出格式:
在一行中輸出這行文本對應的單詞的長度,每一個長度之間以空格隔開,行末沒有最後的空格。
輸入樣例:
It's great to see you here.
輸出樣例:
4 5 2 3 3 4

題目分析:

  • 以空格分割單詞計數,輸出每一個單詞的長度
  • 遇到.或者字符串結束'\0' 中止程序

程序實現:

#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:\n");
 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]=='\0'){
   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

二、 題目內容:
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]=='\0'){//結束異或值計算 
   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] == '\0'){
   gpsCode[j]='\0';
   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] = '\0';
    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]='\0';
    }
    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\n",str);
  int length = strlen(str);//sizeof(str)/sizeof(str[0]);
  int gpsCode = calculateGPSCode(str,length);  
  printf("Correct gpsCode is %d\n",gpsCode);
  char realGPSCode[20];
  getGPSCode(realGPSCode,str,length);  
  long realCodeLong = hexToDec(realGPSCode);
  printf("Real GPSCode is %d\n",realCodeLong);
  
  if(realCodeLong == (long)gpsCode){
   int isContainsGPS = isContainsGPSInfo(str,length);
   printf("isContainGPS:%d \n",isContainsGPS);
   if(isContainsGPS == 1){
    char utcTime[20];
    getUTCTime(utcTime,str,length);
    char beiJingTime[10];
    getBeiJingTime(beiJingTime,utcTime);
    printf("utcTime:%s\n",utcTime); 
    printf("Success!\n"); 
    printf("BeiJingTime is %s\n",beiJingTime);    
   }      
  }else {
   printf("Invalid GPS reenter:\n");   
  }    
  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
相關文章
相關標籤/搜索