Trong C, việc khai báo lại (redeclaration) biến phụ thuộc vào liên kết (linkage) và khởi tạo. Biến cục bộ (local) không cho phép khai báo lại, biến toàn cục (global) cho phép trong một số trường hợp.
Biến cục bộ (Local) - KHÔNG được khai báo lại
int main() {
int a;
int a = 50; // LỖI: redeclaration of 'a' with no linkage
return 0;
}
Biến toàn cục (Global) - Có thể khai báo lại (tentative definition)
Trường hợp 1: Khai báo không khởi tạo, sau đó định nghĩa
#include <stdio.h>
int a; // Tentative definition (khai báo thử)
int a = 50; // Định nghĩa thực sự + khởi tạo
int main() {
printf("a = %d\n", a); // 50
}
Trường hợp 2: Nhiều khai báo không khởi tạo (Tentative definitions)
int a; // OK
int a; // OK - cùng một tentative definition
int a; // OK
int main() { }
Trường hợp 3: Hai lần KHỞI TẠO (initialization) - LỖI
int a = 10; // Định nghĩa + khởi tạo
int a = 50; // LỖI: redefinition of 'a'
Kết quả
[Error] redefinition of 'a'
Quy tắc tóm tắt (C Standard)
| Trường hợp | Kết quả |
|---|---|
Nhiều extern int a; | ✅ OK (khai báo external) |
Nhiều int a; (không init) | ✅ OK (tentative definition - gộp thành 1) |
int a; rồi int a = 10; | ✅ OK (khai báo rồi định nghĩa) |
int a = 10; rồi int a = 20; | ❌ LỖI (nhiều định nghĩa có khởi tạo) |
int a = 10; trong 2 file .c | ❌ LỖI link (multiple definition) |
Best Practice
- File header (
.h): Chỉ khai báoextern int a; - File nguồn (
.c): Định nghĩa duy nhấtint a = 0; - Tránh tentative definition - không rõ ràng
Ví dụ chuẩn multi-file
// globals.h
#ifndef GLOBALS_H
#define GLOBALS_H
extern int g_counter; // Chỉ khai báo
#endif
// globals.c
#include "globals.h"
int g_counter = 0; // Định nghĩa duy nhất
// main.c
#include "globals.h"
#include <stdio.h>
int main() {
g_counter++;
printf("%d\n", g_counter);
}
C++: One Definition Rule (ODR)
C++ nghiêm ngặt hơn: Mọi biến/ hàm phải có duy nhất một định nghĩa trong toàn chương trình. inline variable (C++17) cho phép định nghĩa trong header.