久久久久久久av_日韩在线中文_看一级毛片视频_日本精品二区_成人深夜福利视频_武道仙尊动漫在线观看

IOS開發之路--C語言存儲方式和作用域

只有你完全了解每個變量或函數存儲方式、作用范圍和銷毀時間才可能正確的使用這門語言。今天將著重介紹C語言中變量作用范圍、存儲方式、生命周期、作用域和可訪問性。

概述

基本上每種語言都要討論這個話題,C語言也不例外,因為只有你完全了解每個變量或函數存儲方式、作用范圍和銷毀時間才可能正確的使用這門語言。今天將著重介紹C語言中變量作用范圍、存儲方式、生命周期、作用域和可訪問性。

變量作用范圍 存儲方式 可訪問性

變量作用范圍

在C語言中變量從作用范圍包括全局變量和局部變量。全局變量在定義之后所有的函數中均可以使用,只要前面的代碼修改了,那么后面的代碼中再使用就是修改后的值;局部變量的作用范圍一般在一個函數內部(通常在一對大括號{}內),外面的程序無法訪問它,它卻可以訪問外面的變量。


//
// main.c
// ScopeAndLifeCycle
//
// Created by Kenshin Cui on 14-7-12.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#include <stdio.h>

int a=1;
void changeValue(){
  a=2;
  printf("a=%d\n",a);
}
int main(int argc, const char * argv[]) {
  int b=1;
  changeValue(); //結果:a=2
  printf("a=%d,b=%d\n",a,b); //結果:a=2,b=1 ,因為changeValue修改了這個全局變量
  return 0;
}

變量存儲方式

C語言的強大之處在于它能直接操作內存(指針),但是要完全熟悉它的操作方式我們必須要弄清它的存儲方式。存儲變量的位置分為:普通內存(靜態存儲區)、運行時堆棧(動態存儲區)、硬件寄存器(動態存儲區),當然這幾種存儲的效率是從低到高的。而根據存儲位置的不同在C語言中又可以將變量依次分為:靜態變量、自動變量、寄存器變量。

靜態變量

首先說一下存儲在普通內存中的靜態變量,全局變量和使用static聲明的局部變量都是靜態變量,在系統運行過程中只初始化一次(在下面的例子中雖然變量b是局部變量,在外部無法訪問,但是他的生命周期一直延續到程序結束,而變量c則在第一次執行完就釋放,第二次執行時重新創建)。


//
// 2.1.c
// ScopeAndLifeCycle
//
// Created by Kenshin Cui on 14-7-12.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#include <stdio.h>

int a=1; //全局變量存儲在靜態內存中,只初始化一次

void showMessage(){
  static int b=1; //靜態變量存儲在靜態內存中,第二次調用不會再進行初始化
  int c=1;
  ++b;
  a+=2;
  printf("a=%d,b=%d,c=%d\n",a,b,c);
}

int main(int argc, const char * argv[]) {
  showMessage(); //結果:a=3,b=2,c=1
  showMessage(); //結果:a=5,b=3,c=1
  return 0;
}

自動變量

被關鍵字auto修飾的局部變量是自動變量,但是auto關鍵字可以省略,因此可以得出結論:所有沒有被static修飾的局部變量都是自動變量。


//
// 1.3.c
// ScopeAndLifeCycle
//
// Created by Kenshin Cui on 14-7-12.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#include <stdio.h>
#include <stdlib.h>

int main(int argc, const char * argv[]) {
  int a=1;
  auto int b=2;
  printf("a=%d,b=%d\n",a,b); //結果:a=1,b=2 ,a和b都是自動變量,auto可以省略
  
  //需要注意的是,上面的自動變量是存儲在棧中,其實還可以存儲到堆中
  char c[]="hello,world!";
  long len=strlen(c)*sizeof(char)+1;//之所以加1是因為字符串后面默認有一個\0空操作符不計算在長度內
  char *p=NULL;//可以直接寫成:char *p;
  p=(char *)malloc(len);//分配指定的字節存放c中字符串,注意由于malloc默認返回“void *”需要轉化
  memset(p,0,len);//清空指向內存中的存儲內容,因為分配的內存是隨機的,如果不清空可能會因為垃圾數據產生不必要的麻煩
  strcpy(p,c);
  printf("p=%s\n",p);//結果:p=hello,world!
  free(p);//釋放分配的空間
  p=NULL;//注意讓p指向空,否則p將會是一個存儲一個無用地址的野指針
  
  
  return 0;
}

當然存儲自動變量的棧和堆其實是兩個完全不同的空間(雖然都在運行時有效的空間內):棧一般是程序自動分配,其存儲結果類似于數據結構中的棧,先進后出,程序結束時由編譯器自動釋放;而堆則是開發人員手動編碼分配,如果不進行手動釋放就只有等到程序運行完操作系統回收,其存儲結構類似于鏈表。在上面的代碼中p變量同樣是一個自動變量,同樣可以使用auto修飾,只是它所指向的內容放在堆上(p本身存放在棧上)。

這里說明幾點:malloc分配的空間在邏輯上連續,物理上未必連續;p必須手動釋放,否則直到程序運行結束它占用的內存將一直被占用;釋放p的過程只是把p指向的空間釋放掉,p中存放的地址并未釋放,需要手動設置為NULL,否則這將是一個無用的野指針;

寄存器變量

默認情況下無論是自動變量還是靜態變量它們都在內存中,不同之處就是自動變量放在一塊運行時分配的特殊內存中。但是寄存器變量卻是在硬件寄存器中,從物理上來說它和內存處在兩個完全不同的硬件中。大家都是知道寄存器存儲空間很小,但是它的效率很高,那么合理使用寄存器變量就相當重要了。什么是寄存器變量呢?使用register修飾的int或char類型的非靜態局部變量是寄存器變量。沒錯,需要三個條件支撐:register修飾、必須是int或char類型、必須是非靜態局部變量。

除了存儲位置不同外,寄存器變量完全符合自動變量的條件,因此它的生命周期其實是和自動變量完全一樣的,當函數運行結束后它就會被自動釋放。由于寄存器空間珍貴,因此我們需要合理使用寄存器變量,只有訪問度很高的變量我們才考慮使用寄存器變量,如果過多的定義寄存器變量,當寄存器空間不夠用時會自動轉化為自動變量。


//
// 1.3.c
// ScopeAndLifeCycle
//
// Created by Kenshin Cui on 14-7-12.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#include <stdio.h>

int main(int argc, const char * argv[]) {
  register int a=1;
  printf("a=%d\n",a);
  return 0;
}

上面我們說到變量的存儲類型,其實在C語言中還有兩種存儲類型:常量存儲區和代碼存儲區,分別用于存儲字符串常量、使用const修飾的全局變量以及二進制函數代碼。

可訪問性

在C語言中沒有其他高級語言public、private等修飾符,來限定變量和函數的有效范圍,但是卻有兩個類似的關鍵字能達到類似的效果:extern和static。

externextern作用于變量

我們知道在C語言中變量的定義順序是有嚴格要求的,要使用變量則必須在使用之前定義,extern用于聲明一個已經存在的變量,這樣一來即使在后面定義一個變量只要前面聲明了,也同樣可以使用。具體的細節通過下面的代碼相信大家都可以看明白:


//
// 2.1.c
// ScopeAndLifeCycle
//
// Created by Kenshin Cui on 14-7-12.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#include <stdio.h>

//如果在main函數下面定義了一個變量a,如果在main上面不進行聲明是無法在main中使用a的;
//同樣如果只進行了extern聲明不進行定義一樣會報錯,因為extern并不負責定義變量a而僅僅是聲明一個已經定義過的變量;
//當然如果說在main上面定義int a;去掉main下面的定義同樣是可以的,相當于在上面定義,但如果兩個地方都定義a的話(main上面的extern去掉),則程序認為上面的定義是聲明,只是省略了extern關鍵字;

//第一種情況,在下面定義,不進行聲明,報錯
int main(int argc, const char * argv[]) {
  
  printf("a=%d\n",a);
  return 0;
}

int a;

//第二種情況,在上面定義,正確
int a;
int main(int argc, const char * argv[]) {
  
  printf("a=%d\n",a);
  return 0;
}


//第三種情況,在下面定義在上面聲明,正確
extern int a;
int main(int argc, const char * argv[]) {
  
  printf("a=%d\n",a);
  return 0;
}

int a;


//第四種情況,只在上面聲明(編譯時沒有問題,因為上面的聲明騙過了編譯器,但運行時報錯,因為extern只能聲明一個已經定義的變量),錯誤
extern int a;
int main(int argc, const char * argv[]) {
  
  printf("a=%d\n",a);
  return 0;
}


//第五種情況,上下同時定義(這種方式是正確的,因為上面的定義會被認為是省略了extern的聲明),正確
int a;
int main(int argc, const char * argv[]) {
  
  printf("a=%d\n",a);
  return 0;
}
int a;
//其實下面的情況也是不會出錯的
int a;
int a;
int main(int argc, const char * argv[]) {
  
  printf("a=%d\n",a);
  return 0;
}
int a;
int a;

//第六種情況,將全局變量聲明為局部變量,但是它的實質還是全局變量,正確
int a;
int main(int argc, const char * argv[]) {
  extern int a;
  printf("a=%d\n",a);
  return 0;
}
int a;

//第七種情況,在函數內部重新定義一個變量a,雖然不會報錯,但是兩個a不是同一個
int a;
int main(int argc, const char * argv[]) {
  int a;
  printf("a=%d\n",a);//注意這里輸出的a其實是內部定義的a,和函數外定義的a沒有關系
  return 0;
}
int a;

如果兩個文件同時定義一個全局變量,那實質上他們指的是同一個變量。從下面的例子可以看出,在main.c中修改了變量a之后message.c中的變量a值也修改了。

需要注意,在上面的代碼中無論在message.h中將a定義前加上extern,還是在main.h中的a定以前加上extern結果都是一樣的,extern同樣適用。和在單文件中一樣,不能兩個定義都添加extern,否則就沒有定義了。如果把message.c中a的定義(或聲明)去掉呢,那么它能否訪問main.c中的全局變量a呢,答案是否定的(這和在一個文件中定義了一個函數在另一個文件不聲明就直接用是類似的)。

extern作用于函數

extern作用于函數就不再是簡單的聲明函數了,而是將這個函數作為外部函數(當然還有內部函數,下面會說到),在其他文件中也可以訪問。但是大家應該已經注意到,在上面的代碼中message.c中的showMessage前面并沒有添加extern關鍵字,在main.c中不是照樣訪問嗎?那是因為這個關鍵字是可以省略的,默認情況下所有的函數都是外部函數。

和作用于變量不同,上面main.c和message.c中的extern都可以省略,在這里extern的作用就是定義或聲明一個外部函數。從上面可以看到在不同的文件中可以定義同一個變量,它們被視為同一個變量,但是需要指出的是外部函數在一個程序中是不能重名的,否則會報錯。

staticstatic作用于變量

其實在前面的例子中我們已經看到static關鍵字在變量中的使用了,在例子中使用static定了一個局部變量,而且我們強調static局部變量在函數中只被初始化一次。那么如果static作用于全局變量是什么效果呢?如果static作用于全局變量它的作用就是定義一個只能在當前文件訪問的全局變量,相等于私有全局變量。

從上面的輸出結果可以看出message.c中的變量a和main.c中的變量a并不是同一個變量,事實上message.c中的變量a只能在message.c中使用,雖然main.c中的變量a是全局變量但是就近原則,message.c會使用自己內部的變量a。當然,上面例子中main.c中的變量a定義成靜態全局變量結果也是一樣的,只是這樣如果還有其他源文件就不能使用a變量了。但是main.c中的a不能聲明成extern,因為main.c不能訪問message.c中的變量a,這樣在main.c中就沒變量a的定義了。

static作用于函數

static作用于函數和作用于變量其實是類似的,如果static作用于函數則這個函數就是內部函數,其他文件中的代碼不可以訪問。下面的代碼在運行時會報錯,因為mesage.c中的showMessage()函數是私有的,在main.c中盡管進行了聲明,可以在編譯階段通過,但是在鏈接階段會報錯。

總結

最后做一下簡單總結一下:

extern作用于變量時用于聲明一個已經定義的變量,但是并不能定義變量;使用extern你可以在其他文件中使用全局變量(當然此時extern可以省略); extern作用于函數時與它作用于全局變量有點類似,聲明這個函數是外部函數,其他文件可以訪問,但不同的是當它作用于函數時不僅可以聲明函數還可以定義函數(用在函數定義前面),不管是定義還是聲明都可以省略,C語言默認認為函數定義或聲明都是外部函數; static作用于變量時,該變量只會定義一次,以后在使用時不會重新定義,當static作用于全局變量時說明該變量只能在當前文件可以訪問,其他文件中不能訪問; static作用于函數時與作用于全局變量類似,表示聲明或定義該函數是內部函數(又叫靜態函數),在該函數所在文件外的其他文件中無法訪問此函數;

【網站聲明】本站部分內容來源于互聯網,旨在幫助大家更快的解決問題,如果有圖片或者內容侵犯了您的權益,請聯系我們刪除處理,感謝您的支持!

相關文檔推薦

這篇文章主要介紹了ios實現文件對比的方法,主要是用到了filemanager,有需要的小伙伴可以參考下。
這篇文章主要給大家匯總了一下ios下移動文件方法,從簡單到復雜,十分的實用,有需要的小伙伴可以參考下。
本文給大家總結了幾則在IOS系統下刪除文件的代碼,十分的實用,有需要的小伙伴可以參考下。
本文給大家分享的是IOS開發中遇到的一個手風琴特效無法正常工作的問題的解決方法,經過度娘了很久才找到解決方法,這里推薦給大家,有需要的小伙伴可以參考下。
這篇文章主要介紹了iOS、Mac OS X系統中編程實現漢字轉拼音的方法(超級簡單),本文講解的方法不僅支持中文,還支持日文、韓文等,需要的朋友可以參考下
這篇文章主要分享了iOS7 毛玻璃特效代碼,非常的實用,做IOS開發的童鞋們不要錯過了
主站蜘蛛池模板: 天堂久久网 | 日日夜夜91 | 婷婷成人在线 | www.玖玖玖 | 欧美一区免费 | 国产精产国品一二三产区视频 | 国产一区二区视频免费在线观看 | 国产一区二区日韩 | 999久久久久久久 | 国产精品一区二区无线 | 欧美午夜一区二区三区免费大片 | 99在线资源 | 国内自拍偷拍一区 | 99精品久久久国产一区二区三 | 97久久精品午夜一区二区 | 亚洲第一视频网 | 伊人国产精品 | 日本人麻豆| 亚洲黄色视屏 | 91黄在线观看 | 欧美不卡| av一二三区 | 欧美精品久久久久久 | 欧洲精品久久久久毛片完整版 | 在线观看日韩精品视频 | 免费小视频在线观看 | 国产精品1区 | 一区二区在线免费观看视频 | 久青草影院 | 97成人免费| 中文字幕黄色大片 | 中文字幕亚洲专区 | 成人a免费| 97精品久久 | 久草在线视频中文 | 国产精品免费在线 | 欧美视频在线播放 | 国产成人免费一区二区60岁 | 久久国产一区 | 九九热精| 日韩在线视频观看 |