Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

The #line preprocessor directive overrides the compiler’s internal line numbering and filename tracking during the preprocessing phase. When encountered, it alters the values of the predefined macros __LINE__ and __FILE__ for all subsequent source code lines in the current translation unit.

Purpose and Real-World Usage

While rarely written manually by developers, #line is a critical mechanism for code generators, transpilers, and parser generators (such as lex, yacc, and bison). It allows these tools to map compiler diagnostics (errors and warnings) and debugging symbols from the generated C code back to the original source language files. This ensures developers can debug their actual source code rather than the intermediate C output generated by the tool.

Syntax

The directive accepts two primary forms:
#line digit-sequence
#line digit-sequence "filename"
  • digit-sequence: A positive decimal integer constant. This value dictates the line number assigned to the next line of source code immediately following the directive.
  • "filename" (Optional): A string literal representing the new file name. If omitted, the current value of __FILE__ remains unchanged.

Mechanics and Behavior

When the preprocessor evaluates a #line directive, it modifies the internal state used to generate compiler diagnostics and debugging symbols.
  1. Line Incrementing: The line immediately following the directive evaluates to the specified digit-sequence. Subsequent lines (including blank lines and comments) increment sequentially by 1 from this new baseline, just as they would in standard compilation.
  2. File Reassignment: If the string literal is provided, the __FILE__ macro evaluates to this new string for all subsequent lines until another #line directive changes it or the translation unit ends.
  3. Macro Expansion: The tokens following the #line directive are subject to macro expansion. If the arguments are not explicitly an integer and an optional string, the preprocessor will attempt to expand existing macros to resolve them into the required syntax.

Code Visualization

The following demonstrates how the internal state shifts during preprocessing, accounting for comments and blank lines:
#include <stdio.h>

int main(void) {
    // Standard compilation state
    printf("Line: %d, File: %s\n", __LINE__, __FILE__); 

#line 100 "virtual_file.c"
    // This comment becomes line 100. __FILE__ is now "virtual_file.c"
    printf("Line: %d, File: %s\n", __LINE__, __FILE__); // Evaluates to 101
    
    // The blank line above is 102. This comment is 103.
    printf("Line: %d, File: %s\n", __LINE__, __FILE__); // Evaluates to 104

#line 50
    // This comment becomes line 50. __FILE__ remains "virtual_file.c"
    printf("Line: %d, File: %s\n", __LINE__, __FILE__); // Evaluates to 51

    return 0;
}

Macro Resolution Example

Because #line supports macro expansion, the arguments can be dynamically constructed via #define directives prior to evaluation. Note that the line immediately following the directive receives the new line number:
#define START_LINE 2000
#define TARGET_FILE "core_module.c"

// The preprocessor expands the macros before applying the directive
#line START_LINE TARGET_FILE 
int x = __LINE__; // x is initialized to 2000 (this is the immediate next line)
Master C with Deep Grasping Methodology!Learn More