Vous êtes sur la page 1sur 15

C# DESDE EL PUNTO DE VISTA DE C

C# es un lenguaje de programación bastante semejante en lo que a sintaxis se refiere a C++. Por otra parte,
C++ puede considerarse como un superconjunto de C.

En este documento analizaremos diversos ejemplos de programación utilizados habitualmente en C, y su


correspondiente codificación en C#.

Por lo tanto, este documento no pretende ser un estudio exhaustivo de los tipos de datos, estructuras de
control y librerías de C#, sino una muestra de un conjunto de ejemplos de código escritos en ambos lenguajes.
No se trata entonces de justificar todo cuanto se escribe, con cuidado de haber dado previamente las
definiciones necesarias, sino de mostrar de forma intuitiva las diferencias sintácticas de ambos lenguajes en
aquellos aspectos en que nos encontramos familiarizados en C. En otras palabras, con esto se quiere justificar
la posible falta de rigor expositivo.

Todo lo que aquí se muestra, se muestra de forma muy simple, procurando huir de elementos muy
diferenciales. La forma de proceder utilizada en C# para estos ejemplos no es la más adecuada, sino la más
aproximada a C, sin exponer en ningún caso otras alternativas mejores disponibles.

Se parte de la base de que el alumno “sabe C”, y este documento es una primera toma de contacto con el
nuevo lenguaje partiendo de ese supuesto. Por lo tanto, si el alumno no comprende los ejemplos propuestos
en C, debería repasar “un poco” los contenidos de las asignaturas previas.

Para simplificar el código al máximo y quedarnos con lo esencial en lo que a sintaxis se refiere, todos los
ejemplos están realizados en modo consola, tanto en C como en C#.

1. Hola mundo.
Hola mundo en C Hola mundo en C#
#include <stdio.h> using System;

void main (int argc, char* argv[]) namespace Simple


{ {
printf ("Hola mundo\n"); class CSimple
} {
[STAThread]
static void Main(string[] args)
{
Console.Write ("Hola mundo\n");
}
}
}

El ejemplo de la izquierda muestra un típico programa ‘Hola mundo’ en C. Es necesaria la directiva #include
para poder usar la librería que define la función printf. La función main recibe el número de argumentos
pasados por la línea de comandos, y el array de cadenas de caracteres que los contiene.

El ejemplo de la derecha muestra un típico programa ‘Hola mundo’ escrito en C#. Es necesaria la línea using
System para acceder a la clase Console y al método Write. La función Main en este caso recibe
directamente un array de strings (cadenas de caracteres en C#), como en el caso anterior. El número de
argumentos está implícito en el array de C#.

En C#, además, es necesario un espacio de nombres (namespace Simple) y una clase (class CSimple),
para poder contener la función Main, que será el punto de entrada del programa al ejecutarse.

Desde un punto de vista práctico y, si se quiere, simplista, dentro de la clase CSimple pondremos todo lo que
pondríamos en el fichero fuente de C a excepción de las directivas include y define.
2. Declaración de variables elementales e impresión de información por pantalla.
Ejemplo en C
#include <stdio.h>

void main (int argc, char* argv[])


{
int i=4;
double k=3.74;
char C[] = "Hola que tal";

printf ("i = %d, k = %f, C = %s\n",i,k,C);


}
Ejemplo en C#
using System;

namespace Simple
{
class CSimple
{
[STAThread]
static void Main(string[] args)
{
int i = 4;
double k = 3.74;
string C = "Hola que tal";

Console.Write ("i = {0}, k = {1}, C = {2}\n",i,k,C);


Console.WriteLine ("i = " + i + ", k = " + k + ", C = " + C);
}
}
}

Vemos que las declaraciones de variables (por ahora) son semejantes en ambos lenguajes. Se ha declarado
en C un array de caracteres, aunque no es un tipo simple, para comprobar su equivalencia con el tipo string
de C#.

En C# se ha hecho la impresión, usando el método Write de Console, de dos formas diferentes: La primera
usa {0}, {1}, etc. para indicar los argumentos a imprimir, mostrando su semejanza con los %d y %f, de C. La
segunda usa el operador de concatenación de strings (+), para unir los fragmentos que se desean imprimir.

Como vemos, las variables se declaran en C# de forma semejante a C, y el método Console.Write es el


equivalente al printf, con algunas diferencias de formato.
3. Entrada de datos por la consola (teclado). Conversión de tipos.
Ejemplo en C
#include <stdio.h>

void main (int argc, char* argv[])


{
char Cadena[256];
int DatoEntero;
float DatoReal;

printf ("Introduce tu nombre: ");


scanf ("%s",Cadena);

printf ("Introduce tu edad: ");


scanf ("%d",&DatoEntero);

printf ("Introduce el precio de una barra de pan: ");


scanf ("%f",&DatoReal);

printf ("Tu nombre: %s\n",Cadena);


printf ("Tu edad: %d\n",DatoEntero);
printf ("Barra pan: %f\n",DatoReal);
}
Ejemplo en C#
using System;

namespace Simple
{
class CSimple
{
[STAThread]
static void Main(string[] args)
{
string Cadena;
int DatoEntero;
double DatoReal;

Console.Write ("Introduce tu nombre: ");


Cadena = Console.ReadLine();
Console.Write ("Introduce tu edad: ");
DatoEntero = Convert.ToInt32 (Console.ReadLine());
Console.Write ("Introduce el precio de una barra de pan: ");
DatoReal = Convert.ToDouble (Console.ReadLine ());

Console.WriteLine ("Tu nombre: " + Cadena);


Console.WriteLine ("Tu edad: " + DatoEntero.ToString());
Console.WriteLine ("Barra pan: " + DatoReal.ToString("0.00"));
}
}
}

Vemos que toda la entrada por la consola se realiza a través de la función ReadLine de Console. ReadLine
lee hasta que llega un <CR> (ENTER) del teclado, y retorna siempre un string. Es como si utilizáramos
scanf (“%s”,Cad) siempre, y luego convirtiéramos la cadena de caracteres (con las funciones atoi y
atof).

Las operaciones de la clase Convert permiten convertir los strings leídos por el teclado al tipo de datos
deseado.
4. Operadores.
En general, los operadores de C son semejantes a los de C#. Tenemos la suma, la resta, el producto, la
división (+, -, *, /, %), también operadores de comparación (==, !=, >, …), operadores de bits, etc. La siguiente
tabla resume los operadores de C#.

Categorías Operadores
Aritméticos + - * / %
Lógicos (booleanos y bit a bit) & | ^ ! ~ && || false true
Concatenación de cadenas +
Incremento y decremento ++ --
Desplazamiento << >>
Relacionales == != < > <= >=
Asignación = += -= *= /= %= &= |= ^= <<= >>=
Acceso a miembros .
Indización []
Conversión de tipos explícita ()
Condicional ?:
Concatenación y eliminación de delegados + -
Creación de objetos new
Información de tipos is sizeof typeof
Control de excepciones de desbordamiento checked unchecked
Direccionamiento indirecto y dirección * -> [] &

5. Sentencias de control y palabras reservadas.


Las sentencias de control también son básicamente las mismas. C# tiene algunas más, que ya iremos viendo,
pero por ahora no son relevantes.

Un if se escribe igual en C que en C#, un for y un while también, etc. Por el momento no debemos
prestarle más atención a este tema, aunque debemos saber que C# es más potente que C en este aspecto.

La lista de palabras reservadas de C# es la siguiente:

abstract event new struct


as explicit null switch
base extern object this
bool false operator throw
break finally out true
byte fixed override try
case float params typeof
catch for private uint
char foreach protected ulong
checked goto public unchecked
class if readonly unsafe
const implicit ref ushort
continue in return using
decimal int sbyte virtual
default interface sealed volatile
delegate internal short void
do is sizeof while
double lock stackalloc
else long static
enum namespace string
6. Declaración y recorrido básico de arrays.
Ejemplo en C
#include <stdio.h>

void main (int argc, char* argv[])


{
int Numeros[5];
int i;

for (i=0 ; i<5 ; i++) Numeros[i] = i;


for (i=0 ; i<5 ; i++) printf ("%d ",Numeros[i]);
}
Ejemplo en C#
using System;

namespace Simple
{
class CSimple
{
[STAThread]
static void Main(string[] args)
{
int[] Numeros = new int[5];
int i;

for (i=0 ; i<5 ; i++) Numeros[i] = i;


for (i=0 ; i<5 ; i++) Console.Write ("{0} ",Numeros[i]);
}
}
}

Como vemos en este caso, el manejo de arrays es bastante semejante en ambos lenguajes. La diferencia
fundamental esta en la declaración, como puede verse claramente. En el caso de C#, un array ha de
“construirse”, con el operador new. Por ahora no vamos a ahondar en el tema de la “construcción”, basta con
“creer” que es así, y utilizarlo, y dentro de poco tiempo lo entenderemos mejor.

Ejemplo en C
#include <stdio.h>

void main (int argc, char* argv[])


{
int Numeros[] = {1,2,3,4,5};
int i;

for (i=0 ; i<5 ; i++) printf ("%d ",Numeros[i]);


}
Ejemplo en C#
using System;

namespace Simple
{
class CSimple
{
[STAThread]
static void Main(string[] args)
{
int[] Numeros = new int[] {1,2,3,4,5};

for (int i=0 ; i<5 ; i++) Console.Write ("{0} ",Numeros[i]);


}
}
}

En el ejemplo anterior se muestra la declaración estática de un array, en ambos lenguajes. Vemos también la
declaración de la variable de control dentro del for, algo muy habitual en el estilo de programación de C#.
7. Declaración y llamadas a funciones.
Ejemplo en C
#include <stdio.h>

int UnaFuncion (int p, int q)


{
return (p+q);
}

void main (int argc, char* argv[])


{
int Resultado;

Resultado = UnaFuncion (7,2);


printf ("%d\n",Resultado);
}
Ejemplo en C#
using System;

namespace Simple
{
class CSimple
{
static int UnaFuncion (int p, int q)
{
return (p+q);
}

[STAThread]
static void Main(string[] args)
{
int Resultado = UnaFuncion (7,2);
Console.WriteLine (Resultado);
}
}
}

Vemos que tanto la declaración y las llamadas a funciones son muy semejantes en ambos lenguajes. En C# se
puede inicializar una variable con el resultado de una expresión ejecutable, a diferencia de C (en C++ esto
también está permitido).
8. Paso de parámetros por referencia.
Ejemplo en C
#include <stdio.h>

void UnaFuncion (int* p)


{
*p = *p + 2;
}

void main (int argc, char* argv[])


{
int Dato = 7;

UnaFuncion (&Dato);
printf ("%d\n",Dato);
}
Ejemplo en C#
using System;

namespace Simple
{
class CSimple
{
static void UnaFuncion (ref int p)
{
p = p + 2;
}

[STAThread]
static void Main(string[] args)
{
int Dato = 7;
UnaFuncion (ref Dato);
Console.WriteLine (Dato);
}
}
}

Un uso muy común de los punteros en C es el paso de parámetros por referencia. Sin embargo, en C# los
punteros son considerados como “algo no seguro”, y hay otras maneras de efectuar esta operación. Vemos en
el ejemplo que el parámetro se marca con el modificador ‘ref’, y que también se indica al invocar la función.
Para enviar parámetros por referencia, ya no son necesarios los punteros, y no han de utilizarse para ello.

El paso de estructuras por referencia también es diferente en C# con respecto a C. En C# es preferible utilizar
objetos (elementos de clases) en lugar de estructuras, y en este caso, el paso de parámetros es siempre por
referencia, aunque no se indique. Esto lo veremos en un ejemplo posterior.
9. Uso de punteros.
Ya se ha comentado anteriormente que no es necesario utilizar punteros en C#. Si aún así queremos seguir
utilizándolos (no en esta asignatura), es necesario marcar las funciones que los usen como “no seguras”.

Ejemplo en C#
using System;

namespace Simple
{
class CSimple
{
unsafe static void UnaFuncion (int* p)
{
*p = *p + 2;
}

[STAThread]
unsafe static void Main(string[] args)
{
int Dato = 7;
UnaFuncion (&Dato);
Console.WriteLine (Dato);
}
}
}

Para que el ejemplo anterior funcione, se ha de compilar el proyecto con el parámetro /unsafe, estableciendo
la propiedad de código no seguro de la siguiente manera:

En resumen, evitaremos usar punteros.


10. Definición de estructuras.
Ejemplo en C
#include <stdio.h>
#include <string.h>

typedef struct
{
char Nombre[80];
int Edad;
} Persona;

void main (int argc, char* argv[])


{
Persona P;

P.Edad = 39;
strcpy (P.Nombre,"Ruben Hidalgo");
printf ("%d - %s\n",P.Edad,P.Nombre);
}
Ejemplo en C#
using System;

namespace Simple
{
class CSimple
{
class Persona
{
public string Nombre;
public int Edad;
}

[STAThread]
static void Main(string[] args)
{
Persona P = new Persona();

P.Edad = 39;
P.Nombre = "Ruben Hidalgo";
Console.WriteLine ("{0} - {1}",P.Edad,P.Nombre);
}
}
}

En C# son preferibles las clases a las estructuras. En el ejemplo anterior vemos la declaración de la clase
Persona, semejante a la estructura equivalente de C. Los miembros de la clase deben ser públicos (public)
para que la clase sea “equivalente” a la estructura.

Salvo en la declaración de la variable de tipo Persona (revisar el ejemplo anterior que mostraba la declaración
de un array), el uso de una clase definida de este modo y una estructura es prácticamente el mismo.
11. Paso de estructuras/objetos por parámetro.
Ejemplo en C
#include <stdio.h>
#include <string.h>

typedef struct
{
char Nombre[80];
int Edad;
} Persona;

void TratarPersona (Persona* X)


{
X->Edad++;
}

void main (int argc, char* argv[])


{
Persona P;

P.Edad = 39;
strcpy (P.Nombre,"Ruben Hidalgo");
printf ("%d - %s\n",P.Edad,P.Nombre);
TratarPersona (&P);
printf ("%d - %s\n",P.Edad,P.Nombre);
}
Ejemplo en C#
using System;

namespace Simple
{
class CSimple
{
class Persona
{
public int Edad;
public string Nombre;
}

static void TratarPersona (Persona X)


{
X.Edad++;
}

[STAThread]
static void Main(string[] args)
{
Persona P = new Persona();

P.Edad = 39;
P.Nombre = "Ruben Hidalgo";
Console.WriteLine ("{0} - {1}",P.Edad,P.Nombre);
TratarPersona (P);
Console.WriteLine ("{0} - {1}",P.Edad,P.Nombre);
}
}
}

Tal como se comentaba en un punto anterior, el paso de objetos es siempre por referencia, y vemos en este
ejemplo que la edad de la persona pasada por parámetro queda modificada, tal como en el ejemplo
equivalente en C.

El paso de estructuras por valor funciona en C, siempre y cuando la estructura no contenga ningún puntero o
elemento dinámico, ya que entonces los resultados pueden ser impredecibles. Por este motivo C# no lo
soporta.
12. Arrays dinámicos.
Ejemplo en C
#include <stdio.h>
#include <malloc.h>
#include <string.h>

typedef struct
{
char Nombre[80];
int Edad;
} Persona;

typedef struct
{
Persona* Gente;
int NumPersonas;
} Personas;

void IniPersonas (Personas* X)


{
X->Gente = NULL;
X->NumPersonas = 0;
}

void FinPersonas (Personas* X)


{
free (X->Gente);
X->Gente = NULL;
X->NumPersonas = 0;
}

void AgregarPersona (Personas* X, Persona* P)


{
X->Gente = (Persona*) realloc (X->Gente,sizeof(Persona)*(X->NumPersonas+1));
X->Gente[X->NumPersonas++] = *P;
}

void EliminarPersona (Personas* X, int Indice)


{
int i;

for (i=Indice ; i<X->NumPersonas-1 ; i++) X->Gente[i] = X->Gente[i+1];


X->Gente = (Persona*) realloc (X->Gente,sizeof(Persona)*(--X->NumPersonas));
}

void ImprimirPersonas (Personas* X)


{
int i;

printf ("Listado de personas:\n");


for (i=0 ; i<X->NumPersonas ; i++)
printf ("%d - %s\n",X->Gente[i].Edad,X->Gente[i].Nombre);
printf ("\n");
}

void main (int argc, char* argv[])


{
Personas K;
IniPersonas (&K);

Persona A;
A.Edad = 15;
strcpy (A.Nombre,"Manolo");
AgregarPersona (&K,&A);

A.Edad = 25;
strcpy (A.Nombre,"Filomeno");
AgregarPersona (&K,&A);

A.Edad = 48;
strcpy (A.Nombre,"Martirio");
AgregarPersona (&K,&A);

ImprimirPersonas (&K);
EliminarPersona (&K,1);
ImprimirPersonas (&K);
FinPersonas (&K);
}

El ejemplo anterior muestra el uso frecuente de un array dinámico de estructuras.


En C, el manejo de este tipo de estructuras involucra malloc (reservar memoria), realloc (reasignar el
espacio asignado), free (liberar espacio), y copias entre elementos.

C# considera estas operaciones como “no seguras”, y ofrece tipos de datos para realizar estas tareas de forma
segura. Además, dichos tipos de datos son mucho más fáciles de utilizar.

Ejemplo en C#
using System;
using System.Collections;

namespace Simple
{
class CSimple
{
class Persona
{
public int Edad;
public string Nombre;
}

static void ImprimirPersonas (ArrayList K)


{
Console.WriteLine ("Listado de personas:");
for (int i=0 ; i<K.Count ; i++)
{
Persona P = (Persona) K[i];
Console.WriteLine ("{0} - {1}",P.Edad,P.Nombre);
}
Console.WriteLine ();
}

[STAThread]
static void Main(string[] args)
{
ArrayList K = new ArrayList ();

Persona P = new Persona();


P.Edad = 15;
P.Nombre = "Manolo";
K.Add (P);

P = new Persona();
P.Edad = 25;
P.Nombre = "Filomeno";
K.Add (P);

P = new Persona();
P.Edad = 48;
P.Nombre = "Martirio";
K.Add (P);

ImprimirPersonas (K);
K.RemoveAt (1);
ImprimirPersonas (K);
}
}
}

Vemos que un objeto ArrayList tiene las operaciones necesarias para agregar y eliminar elementos, igual
que el tratamiento implementado por malloc, realloc y free. Un objeto ArrayList “se inicializa solo” y
“se destruye solo”. Pero esto es un tema sobre el que incidiremos profundamente cuando veamos la
programación orientada al objeto, ahora no es relevante.
13. Acceso a ficheros de texto en modo lectura.
Ejemplo en C
#include <stdio.h>
#include <stdlib.h>

void main (int argc, char* argv[])


{
FILE* F;
char c;

F = fopen ("C:\\Texto.txt","r");
while (!feof(F))
{
fscanf (F,"%c",&c);
printf ("%c",c);
}
fclose (F);
}
Ejemplo en C#
using System;
using System.IO;

namespace Simple
{
class CSimple
{
[STAThread]
static void Main(string[] args)
{
FileStream F = new FileStream ("C:\\Texto.txt",FileMode.Open,
FileAccess.Read);
while (F.Position < F.Length)
{
char c = (char) F.ReadByte ();
Console.Write (c);
}
F.Close ();
}
}
}

El ejemplo anterior muestra cómo leer carácter a carácter un fichero de texto. Aquí terminan las semejanzas
entre C y C# en este aspecto. La función fscanf permite ir leyendo datos separados por espacios,
convirtiendo los tipos de la forma deseada. Sin embargo, un objeto FileStream sólo permite leer caracteres o
bloques de ellos, no es capaz de ir leyendo de separador en separador.

Ejemplo en C#
static void Main(string[] args)
{
StreamReader F = new StreamReader ("C:\\Texto.txt");
for (;;)
{
string s = F.ReadLine ();
if (s == null) break;
Console.WriteLine ("Linea: " + s);
}
F.Close ();
}

El ejemplo anterior usa un objeto StreamReader para leer el fichero de texto, línea a línea.
Ejemplo en C#
using System;
using System.IO;

namespace Simple
{
class CSimple
{
[STAThread]
static void Main(string[] args)
{
StreamReader F = new StreamReader ("C:\\Texto.txt");
for (;;)
{
string s = F.ReadLine ();
if (s == null) break;

Console.WriteLine ("Linea: " + s);


string[] Palabras = s.Split (' ');
for (int i=0 ; i<Palabras.Length ; i++)
Console.WriteLine (" " + Palabras[i]);
}
F.Close ();
}
}
}

El ejemplo anterior va leyendo del fichero línea a línea, y utiliza el método Split del objeto string para
separar las palabras en un nuevo array de cadenas de caracteres. La salida de este programa muestra línea a
línea el fichero, y para cada línea muestra las palabras, separadas unas de otras.

A partir de aquí, y con las operaciones del objeto Convert (Convert.ToInt32, Convert.ToDouble, etc),
se puede emular el funcionamiento de un fscanf con formato.
14. Acceso a ficheros de texto en modo escritura.
Ejemplo en C
#include <stdio.h>
#include <stdlib.h>

void main (int argc, char* argv[])


{
FILE* F;
int i = 4;
double j = 45.67;
char k[] = "Hola";

F = fopen ("C:\\TextoSalida.txt","wt");
fprintf (F,"Esto es una cosa a escribir\n");
fprintf (F,"Esto es otra cosa, que tambien salta de linea\n");
fprintf (F,"Impresion de datos: %d %f %s\n",i,j,k);
fclose (F);
}
Ejemplo en C#
using System;
using System.IO;

namespace Simple
{
class CSimple
{
[STAThread]
static void Main(string[] args)
{
StreamWriter F = new StreamWriter ("C:\\TextoSalida.txt");

F.Write ("Esto es una cosa a escribir\r\n");


F.WriteLine ("Esto es otra cosa, que tambien salta de linea");

int i = 4;
double j = 45.67;
string k = "Hola";
F.WriteLine ("Impresion de datos: {0} {1} {2}",i,j,k);
F.Close ();
}
}
}

StreamWriter es el tipo de datos (objeto) de C# que permite imprimir en un fichero de texto de forma
semejante a fprintf en C.

Vous aimerez peut-être aussi