🏷️ C Identifiers

Identifiers in C Programming Language

Every rule for naming variables, functions, and other entities in C — compiler-enforced rules, professional naming conventions, scope, lifetime, and the reserved patterns even experienced programmers occasionally forget.

📅

Last Updated

March 2026

⏱️

Read Time

23 min

🎯

Level

Beginner

What is an Identifier in C?

An identifier is a name you, the programmer, invent to label something in your C program — a variable, a function, an array, a struct, a macro, or any other user-defined entity. Every time you write int totalMarks; or void calculateArea(void);, the words totalMarks and calculateArea are identifiers: names that exist purely because you chose them, carrying no built-in meaning to the compiler beyond whatever you use them to represent.

This is precisely what separates an identifier from a keyword. A keyword like int or return has a fixed, unchangeable meaning baked into the C language itself. An identifier has no inherent meaning at all — totalMarks could just as easily be named tm, x, or studentScoreSum, and the program would behave identically. The compiler does not care what you name things; it only cares that your naming follows a small set of strict rules, and that every identifier you use was properly declared before you use it.

Despite this freedom, identifiers are arguably the single most important stylistic choice you make as a programmer. Code is read far more often than it is written, and a well-chosen identifier like studentAverageGrade instantly communicates intent, while a poorly chosen one like x2 forces every future reader — including you, six months later — to reverse-engineer what it was meant to represent. This chapter covers both halves of that story: the rules the compiler enforces, and the conventions professionals follow that the compiler does not enforce at all.

Compiler-Enforced Rules for Identifiers

C defines a small, strict set of rules that every identifier must follow. Violating any of these produces an immediate compile-time error — there is no flexibility or interpretation involved; the compiler checks these rules mechanically, character by character.

  • Rule 1 — Allowed characters only — An identifier may contain only uppercase letters (A-Z), lowercase letters (a-z), digits (0-9), and the underscore (_). No spaces, hyphens, punctuation, or symbols like @, $, or % are permitted anywhere in an identifier.

  • Rule 2 — Cannot begin with a digit — The very first character must be a letter or an underscore. total1 is valid; 1total is not, because the compiler needs to distinguish the start of a name from the start of a numeric constant unambiguously.

  • Rule 3 — Case sensitivity — C treats uppercase and lowercase letters as completely distinct. total, Total, and TOTAL are three entirely different identifiers as far as the compiler is concerned, and can even coexist as three separate variables in the same scope.

  • Rule 4 — Cannot match a reserved keyword — None of C's 32+ reserved keywords (int, if, return, struct, and so on) can be used as an identifier, since the compiler has already assigned them a fixed grammatical meaning.

  • Rule 5 — Significant character limit — The C standard guarantees at least 31 significant characters are recognised for identifiers with internal linkage, and at least 63 for those with external linkage. In practice, every modern compiler (GCC, Clang, MSVC) supports far longer names without any practical limitation.

  • Rule 6 — Must be unique within its scope — Two identifiers with the same name cannot both be declared in the exact same scope (for example, two variables both named count inside the same function block), though the same name can be reused safely in separate, non-overlapping scopes.

🖥️ Cvalid_vs_invalid.c
int totalMarks = 90;      // ✅ Valid: letters only, starts with a letter
int _tempValue = 5;      // ✅ Valid: starts with underscore (legal, though discouraged)
int student_1 = 1;        // ✅ Valid: letters, digit, underscore
int Count, count, COUNT;  // ✅ Valid: three distinct identifiers (case-sensitive)

// int 2ndPlace = 5;      // ❌ INVALID: starts with a digit
// int my-score = 10;     // ❌ INVALID: hyphen is not allowed
// int total marks = 90;  // ❌ INVALID: contains a space
// int float = 3;         // ❌ INVALID: 'float' is a reserved keyword
// int @rate = 5;         // ❌ INVALID: '@' is not an allowed character

How the Compiler Validates an Identifier — Flowchart

It helps to see exactly what mental checklist the compiler effectively runs through the moment it encounters something that looks like it might be an identifier in your source code.

📝 Candidate Word Founde.g. 'totalMarks', '2ndPlace'
begin validation
🔤 Check First CharacterLetter or underscore?
starts with digit
❌ Reject: Invalid StartCannot begin with a digit
🔍 Check Remaining CharactersOnly letters, digits, underscores?
illegal character found
❌ Reject: Illegal Charactere.g. space, hyphen, symbol
📖 Compare Against Keyword ListMatches int, if, return, etc.?
matches a keyword
🔑 Classify as KEYWORDCannot be reused as a name
✅ Valid IdentifierAccepted into the symbol table

Code Execution Flow — from source to output

Every 'reject' branch in this flowchart corresponds to a real compiler error message you will eventually see, such as 'expected identifier before numeric constant' (Rule 2 violation) or 'expected identifier before int' (Rule 4 violation, when a keyword is mistakenly used as a name). Once an identifier clears every check, it is accepted and added to the compiler's symbol table, ready to be tracked for type and scope throughout the rest of compilation.

Professional Naming Conventions (Not Enforced, But Expected)

Beyond the compiler's strict rules lies a much larger, unenforced world of convention — habits professional C developers follow consistently, not because the compiler demands it, but because clear, predictable naming makes code dramatically easier to read, maintain, and debug.

🐫
camelCase for Variables & Functions

The most common convention in modern C: the first word lowercase, each subsequent word capitalised — totalMarks, calculateAverage(), studentCount. Widely used in application-level and beginner-friendly codebases.

🐍
snake_case as an Alternative

All lowercase, words separated by underscores — total_marks, calculate_average(), student_count. Extremely common in systems programming, the Linux kernel, and much of the C Standard Library itself (e.g. strlen, malloc).

📢
ALL_CAPS for Macros and Constants

#define MAX_STUDENTS 100 and const int DAYS_IN_WEEK = 7; — capitalising constants signals 'this value is fixed' at a glance, instantly distinguishing it from an ordinary variable.

📏
Descriptive Over Short

studentAverageMarks communicates far more than sam or x, even though all three compile identically. The extra few characters of typing cost almost nothing compared to the reading time saved for every future maintainer.

🔡
Consistent Prefixes for Related Groups

Grouping related identifiers with a shared prefix — studentName, studentAge, studentGPA — visually clusters them together and makes autocomplete far more useful in modern editors.

🚫
Avoiding Single-Letter Names (Except Loop Counters)

Single letters like i, j, and k remain an accepted convention specifically for short-lived loop counters, but should be avoided for anything with broader meaning or a longer lifetime in the program.

ConventionExampleMost Common In
camelCasetotalMarks, calculateAreaApplication code, beginner tutorials, many modern C codebases
snake_casetotal_marks, calculate_areaLinux kernel, POSIX APIs, much of the C Standard Library
PascalCaseTotalMarks, CalculateAreaLess common in C; more typical of C++/C# struct/type names
ALL_CAPSMAX_STUDENTS, PI_VALUEMacros (#define) and true constants everywhere
Hungarian notation (legacy)iCount, szName, pPointerOlder Windows API code; largely discouraged in modern style guides

Identifier Scope — Where a Name Is Visible

An identifier's scope is the region of a program where that name can actually be referenced. C defines several distinct kinds of scope, and understanding them prevents both 'undeclared identifier' errors and a subtler class of bugs caused by accidentally shadowing one identifier with another of the same name.

  • Block Scope — An identifier declared inside a pair of curly braces { } — including inside a function, if statement, or loop — is visible only from its point of declaration to the closing brace of that specific block.

  • Function Scope — Applies specifically to labels used with goto — a label is visible anywhere within the function it is declared in, regardless of block nesting, but nowhere outside that function.

  • File Scope (Global Scope) — An identifier declared outside every function, at the top level of a source file, is visible from its declaration point to the end of that file, and potentially to other files too if declared extern.

  • Function Prototype Scope — Parameter names written inside a function's prototype/declaration (not its definition) exist only within that prototype itself, purely for documentation purposes — for example, the total in int add(int total, int extra);.

🖥️ Cscope_example.c
#include <stdio.h>

int globalCount = 100;      // File scope: visible everywhere in this file

void printValues(void) {
    int localCount = 5;    // Block scope: visible only inside this function
    printf("global: %d, local: %d\n", globalCount, localCount);

    {
        int localCount = 99;  // A NEW variable, shadows the outer localCount here
        printf("inner localCount: %d\n", localCount);   // prints 99
    }

    printf("outer localCount: %d\n", localCount);       // prints 5 again
}

int main() {
    printValues();
    // printf("%d", localCount);   // ERROR: localCount not visible here
    return 0;
}

The inner block's localCount in this example shadows the outer one — meaning it temporarily hides the outer variable of the same name for the duration of that inner block, without actually destroying or modifying it. This is completely legal C, but professional style guides generally discourage deliberately reusing names this way, since it can confuse readers (and occasionally the programmer themselves) about which variable a given line is actually referring to.

Linkage and Lifetime — How Long an Identifier Exists

Beyond scope (where a name is visible), C also defines linkage (whether a name refers to the same entity across different scopes or files) and lifetime (how long the underlying storage that identifier refers to actually exists in memory).

Linkage TypeMeaningTypical Example
No linkageEach declaration refers to a unique, unrelated entityA local variable inside a function, e.g. int count;
Internal linkageVisible and shared throughout one source file, but invisible to other filesA static global variable or static function
External linkageVisible and shared across multiple source files in the same programAn ordinary global variable or function, or one declared extern
Lifetime CategoryMeaningTypical Example
AutomaticCreated when its block begins executing, destroyed when the block endsA normal local variable, e.g. int x; inside a function
StaticExists for the entire duration of the program's executionA variable declared static, or any global variable
Dynamic (allocated)Created and destroyed explicitly by the programmer using malloc()/free()Memory obtained via malloc() and released via free()

These two concepts are related to, but distinct from, scope. A static local variable, for instance, has block scope (visible only inside its function) but static lifetime (it retains its value between calls, existing for the entire program's run) — precisely the behaviour shown in the static int calls = 0; example covered in the C Keywords chapter.

Internal vs External Identifiers

The C standard draws a specific distinction between identifiers used purely within a single file (internal identifiers) and those intended to be shared across multiple files in a larger, multi-file project (external identifiers) — a distinction that matters directly for the minimum significant-character guarantees mentioned earlier.

  • Internal identifiers — Names only ever referenced within the single source file where they are declared — most local variables, and any static global variable or static function. The standard guarantees at least 31 significant characters are considered by every conforming compiler.

  • External identifiers — Names intended to be shared and linked across multiple .c files — ordinary (non-static) global variables and functions, typically declared extern in other files that need to use them. The standard guarantees at least 63 significant characters, though historically some older linkers imposed stricter limits, which is partly why this distinction exists at all.

🖥️ Cutils.c — an external identifier
int sharedTotal = 0;         // External identifier: visible to other files via extern

int calculateSum(int a, int b) {   // External identifier: callable from other files
    return a + b;
}
🖥️ Cmain.c — using the external identifier
#include <stdio.h>

extern int sharedTotal;            // Declares: defined elsewhere, but usable here
extern int calculateSum(int, int); // Declares the function's signature

int main() {
    sharedTotal = calculateSum(10, 20);
    printf("Sum: %d\n", sharedTotal);
    return 0;
}

Reserved Identifier Patterns — Names You Should Never Choose

Beyond the formal keyword list, the C standard also reserves certain patterns of identifier names specifically for the compiler and the C Standard Library's own internal use. These are technically legal to write, and many compilers will not immediately reject them — but using them yourself risks silent, hard-to-diagnose conflicts with the implementation.

  • Names beginning with a double underscore (__init) — Reserved everywhere in the program, in every scope, for the compiler and standard library's exclusive use.

  • Names beginning with an underscore followed by an uppercase letter (_Temp) — Also reserved everywhere, following the exact pattern the C11 standard used for its own new keywords like _Bool and _Atomic.

  • Names beginning with a single underscore at file scope (_config) — Reserved specifically for the implementation when declared outside any function, though local variables inside a function with a single leading underscore are technically permitted (if still stylistically discouraged).

  • Standard library identifiers (printf, malloc, size_t, errno) — Not formally 'reserved' the same way, but redefining or shadowing any of these is almost always an unintentional and serious mistake, since it can silently replace behaviour your own code and included headers may still depend on.

The single safest professional habit: never start your own identifiers with an underscore at all. This one rule of thumb sidesteps every reserved-pattern collision described above without requiring you to memorise the precise, somewhat intricate details of which underscore patterns are reserved in which specific scope.

Identifiers vs Keywords — Side-by-Side Comparison

AspectIdentifierKeyword
Who defines itThe programmer, for their own useThe C language standard itself
MeaningNone inherently — entirely defined by how you use itFixed and unchangeable, defined by the grammar
Can be redefined?N/A — you invent it fresh each time❌ Never — permanently reserved
Total count possibleUnlimited — as many as a program needs32 in C89, more added by later standards
ExamplestotalMarks, calculateArea, Studentint, if, return, struct, while
Governed byNaming rules (Rules 1-6) + style conventionsThe ISO C grammar specification directly

Common Identifier Mistakes Beginners Make

✅ Advantages
Descriptive Naming from Day OneBuilding the habit of writing studentAge instead of a or x early on pays off enormously once programs grow beyond a few dozen lines, and costs nothing extra to learn from the start.
One Consistent Convention Per ProjectChoosing either camelCase or snake_case and sticking to it consistently throughout a single project (rather than mixing both) makes the entire codebase easier to scan and predict.
Reserving ALL_CAPS Purely for ConstantsFollowing the widespread convention of using capitals only for macros and true constants means anyone reading MAX_USERS instantly knows, without checking, that this value never changes.
Avoiding Leading Underscores EntirelySidestepping reserved-identifier patterns completely by simply never starting an identifier with an underscore, regardless of the exact technical rule involved.
❌ Disadvantages
Using a Keyword as a Variable NameAttempting int for = 5; or float double = 2.5; fails immediately, since both for and double are reserved keywords — a surprisingly common early mistake before the full keyword list becomes familiar.
Starting an Identifier With a DigitWriting int 3rdAttempt = 1; produces a compile error, since the compiler needs the very first character to unambiguously signal 'this is a name', not the start of a number.
Overly Short, Meaningless NamesNaming everything x, y, temp, or data without any further context makes code technically correct but very difficult for anyone (including your future self) to understand months later.
Mixing Multiple Naming ConventionsSwitching between totalMarks, total_score, and TotalCount within the same small project creates unnecessary visual inconsistency and cognitive overhead for readers.
Relying on Case Sensitivity for Distinct MeaningDeclaring both total and Total as separate variables intended to hold genuinely different things is legal but confusing — a reader skimming quickly can very easily mistake one for the other.

Extended Valid & Invalid Identifier Examples

This reference table walks through a wide variety of borderline and instructive examples, explaining precisely why each one is accepted or rejected by the compiler.

IdentifierValid?Reason
studentMarks✅ ValidLetters only, starts with a letter, follows camelCase
student_marks✅ ValidLetters, underscore separator — valid snake_case style
_privateData✅ Valid (discouraged)Legal syntax, but risks colliding with reserved patterns at file scope
Marks2024✅ ValidDigits are permitted anywhere except as the very first character
2024Marks❌ InvalidBegins with a digit
student-marks❌ InvalidHyphen is not an allowed character in C identifiers
student marks❌ InvalidContains a space, which always terminates a token
int❌ InvalidExactly matches a reserved keyword
Int✅ ValidDifferent capitalisation than the keyword int — C is case-sensitive
total$score❌ InvalidThe dollar sign is not part of C's allowed identifier character set
__reserved✅ Valid syntax, but reservedBegins with a double underscore — reserved for the compiler/library
MAX_LIMIT✅ ValidAll uppercase — conventional style for macros and true constants

How Identifier Rules Have Evolved Across C Standards

While the core identifier rules have remained remarkably stable since C89, later standards refined the details, particularly around how many characters are guaranteed to be significant, and how far Unicode-based names are supported.

StandardMinimum Significant Characters (Internal / External)Notable Identifier-Related Change
C89 / C9031 / 6 (historically, for external linkage)First formal identifier length guarantees; the external limit of only 6 characters, case-insensitive, reflected very old linker limitations
C9963 / 31Significantly raised both guarantees; introduced limited support for universal character names (Unicode escapes) in identifiers
C1163 / 31 (unchanged from C99)Improved and clarified rules for using universal character names within identifiers
C17 / C1863 / 31 (unchanged)No changes to identifier rules — purely a bug-fix and clarification release
C2363 / 31 (unchanged)Improved practical Unicode identifier support, aligning more closely with other modern languages

In practice, every mainstream compiler today (GCC, Clang, MSVC) comfortably exceeds even the C23 minimums by a huge margin, routinely supporting identifiers hundreds of characters long without complaint — these standard-mandated minimums exist purely as a portability guarantee, not a practical ceiling you are ever likely to bump into in ordinary code.

C Identifiers — Interview Questions

Identifier-focused questions are a staple of entry-level interviews and academic viva examinations, since they test whether a candidate truly understands C's naming rules rather than having simply avoided breaking them by accident.

Practice Questions — Test Your Understanding

1. Identify which of these are valid C identifiers, and explain why any invalid ones fail: myAge, 7dwarfs, total-sum, _config, Return, my Var.

Easy

2. What is the difference between the identifiers Count and count in a C program?

Easy

3. Explain why int double = 5; fails to compile, while int Double = 5; compiles successfully.

Medium

4. In the code below, what does each printf statement output, and why? int x = 10; { int x = 20; printf("%d", x); } printf("%d", x);

Medium

5. Why is it considered poor practice, though not illegal, to name your own variable __buffer?

Medium

6. What is the practical difference between an identifier with internal linkage and one with external linkage in a two-file project?

Hard

7. A beginner names every loop counter and temporary variable in a 200-line program x, y, z, a, b, c. What specific problems does this create, beyond simply 'being unclear'?

Hard

8. Why does the C standard guarantee different minimum significant-character counts for internal versus external identifiers (31 vs 63 in modern standards)?

Hard

Conclusion — Naming Well Is a Skill Worth Mastering

Identifiers sit at an interesting intersection in C: the compiler enforces only a small, mechanical set of rules about what characters and patterns are legal, yet the choices you make within that freedom shape how readable, maintainable, and bug-resistant your code becomes far more than almost any other single habit.

You now understand the six compiler-enforced naming rules, the professional conventions (camelCase, snake_case, ALL_CAPS for constants) that experienced C developers follow by habit, the different categories of scope and linkage that determine where and how long an identifier is meaningful, the internal-vs-external identifier distinction tied to multi-file projects, and the reserved underscore patterns the standard sets aside for its own use — knowledge that will save you from an entire category of confusing bugs and cryptic compiler errors.

Concept CoveredWhy It Matters Going Forward
Six naming rulesPrevents an entire class of 'why won't this compile' frustration
Naming conventionsMakes your code readable to yourself and others months later
Scope (block/function/file)Explains exactly where a given name can and cannot be used
Linkage & lifetimeExplains static's dual behaviour and multi-file variable sharing
Reserved underscore patternsAvoids silent collisions with the compiler/standard library
Identifiers vs keywordsPrevents the single most common early beginner naming mistake

The natural next step is C Variables and Data Types, where every identifier you have just learned to name correctly finally gets attached to real, working storage — actual memory that holds values, gets modified, and drives your program's logic.

A name is the very first decision you make about anything in your program — make it count. Clear, rule-following, conventionally styled identifiers are a habit that compounds in value with every additional line of code you write. 🏷️

Frequently Asked Questions (FAQ)