欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時間:2014年08月28日 11:33:13   投稿:hebedich  
只有你完全了解每個變量或函數(shù)存儲方式、作用范圍和銷毀時間才可能正確的使用這門語言。今天將著重介紹C語言中變量作用范圍、存儲方式、生命周期、作用域和可訪問性。

概述

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

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

變量作用范圍

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

//
// 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(); //結(jié)果:a=2
  printf("a=%d,b=%d\n",a,b); //結(jié)果:a=2,b=1 ,因為changeValue修改了這個全局變量
  return 0;
}

變量存儲方式

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

靜態(tài)變量

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

//
// 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; //全局變量存儲在靜態(tài)內(nèi)存中,只初始化一次

void showMessage(){
  static int b=1; //靜態(tài)變量存儲在靜態(tài)內(nèi)存中,第二次調(diào)用不會再進行初始化
  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(); //結(jié)果:a=3,b=2,c=1
  showMessage(); //結(jié)果:a=5,b=3,c=1
  return 0;
}

自動變量

被關(guān)鍵字auto修飾的局部變量是自動變量,但是auto關(guān)鍵字可以省略,因此可以得出結(jié)論:所有沒有被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); //結(jié)果:a=1,b=2 ,a和b都是自動變量,auto可以省略
  
  //需要注意的是,上面的自動變量是存儲在棧中,其實還可以存儲到堆中
  char c[]="hello,world!";
  long len=strlen(c)*sizeof(char)+1;//之所以加1是因為字符串后面默認有一個\0空操作符不計算在長度內(nèi)
  char *p=NULL;//可以直接寫成:char *p;
  p=(char *)malloc(len);//分配指定的字節(jié)存放c中字符串,注意由于malloc默認返回“void *”需要轉(zhuǎn)化
  memset(p,0,len);//清空指向內(nèi)存中的存儲內(nèi)容,因為分配的內(nèi)存是隨機的,如果不清空可能會因為垃圾數(shù)據(jù)產(chǎn)生不必要的麻煩
  strcpy(p,c);
  printf("p=%s\n",p);//結(jié)果:p=hello,world!
  free(p);//釋放分配的空間
  p=NULL;//注意讓p指向空,否則p將會是一個存儲一個無用地址的野指針
  
  
  return 0;
}

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

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

寄存器變量

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

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

//
// 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語言中還有兩種存儲類型:常量存儲區(qū)和代碼存儲區(qū),分別用于存儲字符串常量、使用const修飾的全局變量以及二進制函數(shù)代碼。

可訪問性

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

externextern作用于變量

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

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

#include <stdio.h>

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

//第一種情況,在下面定義,不進行聲明,報錯
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只能聲明一個已經(jīng)定義的變量),錯誤
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;

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

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

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

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

extern作用于函數(shù)

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

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

staticstatic作用于變量

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

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

static作用于函數(shù)

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

總結(jié)

最后做一下簡單總結(jié)一下:

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

相關(guān)文章

  • 關(guān)于iOS自適應(yīng)cell行高的那些事兒

    關(guān)于iOS自適應(yīng)cell行高的那些事兒

    這篇文章主要給大家介紹了關(guān)于iOS自適應(yīng)cell行高的那些事兒,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一看看吧
    2018-11-11
  • iOS App開發(fā)中UIViewController類的使用教程

    iOS App開發(fā)中UIViewController類的使用教程

    UIViewController是iOS中控制視圖的關(guān)鍵所在,這里我們將針對UIViewController的聲明周期與主要屬性和方法,來總結(jié)iOS App開發(fā)中UIViewController類的使用教程
    2016-07-07
  • IOS利用CocoaHttpServer搭建手機本地服務(wù)器

    IOS利用CocoaHttpServer搭建手機本地服務(wù)器

    這篇文章主要介紹了IOS利用CocoaHttpServer搭建手機本地服務(wù)器的步驟,幫助大家更好的理解和學(xué)習(xí)使用ios開發(fā),感興趣的朋友可以了解下
    2021-04-04
  • IOS獲取當前版本號 Bundle ID等信息的方法詳解

    IOS獲取當前版本號 Bundle ID等信息的方法詳解

    這篇文章主要介紹了IOS獲取當前版本號 Bundle ID等信息的方法詳解的相關(guān)資料,需要的朋友可以參考下
    2016-12-12
  • iOS列表上拉(平滑加載數(shù)據(jù))自動加載數(shù)據(jù)的問題解決

    iOS列表上拉(平滑加載數(shù)據(jù))自動加載數(shù)據(jù)的問題解決

    這篇文章主要給大家介紹了關(guān)于iOS列表上拉(平滑加載數(shù)據(jù))自動加載數(shù)據(jù)問題的相關(guān)資料,本文實現(xiàn)的效果很多app都用的這種效果,文中通過圖文以及實例代碼介紹的非常詳細,需要的朋友可以參考下
    2021-07-07
  • IOS 禁止縮放頁面的實現(xiàn)方法

    IOS 禁止縮放頁面的實現(xiàn)方法

    這篇文章主要介紹了IOS 禁止縮放頁面的實現(xiàn)方法的相關(guān)資料,這里主要介紹了IOS 10如何通過設(shè)置來實現(xiàn)禁止縮放及實現(xiàn)方法,需要的朋友可以參考下
    2017-07-07
  • iOS App項目中引入SQLite數(shù)據(jù)庫的教程

    iOS App項目中引入SQLite數(shù)據(jù)庫的教程

    SQLite是一個極輕的嵌入式數(shù)據(jù)庫,在應(yīng)用程序中捆綁使用可以更方便地幫助操控關(guān)系型數(shù)據(jù),這里我們就來看一下iOS App項目中引入SQLite數(shù)據(jù)庫的教程
    2016-06-06
  • iOS中使用UItableviewcell實現(xiàn)團購和微博界面的示例

    iOS中使用UItableviewcell實現(xiàn)團購和微博界面的示例

    這篇文章主要介紹了iOS中使用UItableviewcell實現(xiàn)團購和微博界面的示例,開發(fā)語言基于傳統(tǒng)的Objective-C,需要的朋友可以參考下
    2016-01-01
  • iOS中UIView實現(xiàn)不同方向的導(dǎo)角

    iOS中UIView實現(xiàn)不同方向的導(dǎo)角

    這篇文章主要給大家介紹了關(guān)于iOS中UIView實現(xiàn)不同方向的導(dǎo)角的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學(xué)習(xí)或使用iOS具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-05-05
  • iOS內(nèi)存管理Tagged Pointer使用原理詳解

    iOS內(nèi)存管理Tagged Pointer使用原理詳解

    這篇文章主要為大家介紹了iOS內(nèi)存管理Tagged Pointer使用原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-01-01

最新評論